What would you like?
Apply the existing PR path-filtering mechanism to pushes on develop. Today generate-pipeline-parameters.sh computes which test groups a change actually needs, but a branch override forces every develop merge to run everything. Remove develop from that override and let the measured change decide, exactly as it does on PRs.
Three cases keep running everything, explicitly:
release/* branches (and electron/*, and update-v8-snapshot-cache-on-develop) — unchanged.
- Nightly scheduled pipelines. The platform cron runs on develop, so a plain branch check would path-filter the nightly sweep down to the last merge's diff. Scheduled runs must emit full parameters — the whole point of the nightly is an unfiltered full pass.
- Manual
run-all-jobs=true triggers — unchanged.
Why is this needed?
Develop merges are the second-biggest source of unfiltered spend, and we now have measured data on what they contain. A faithful replay of the CI path classifier over the last 400 first-parent merges to develop (2026-06-03 → 2026-09-02) found:
| Change type |
Share of merges |
| Engine-only (server/proxy/driver, no GUI) |
66.0% |
| Docs/misc |
14.5% |
| Mixed |
7.2% |
| npm-package-only |
6.5% |
| GUI-only |
5.8% |
Two-thirds of develop merges could skip the GUI test block, which is 44.2% of a full pipeline's credits. Docs-only merges could skip nearly everything. Measured against job-level costs, filtering develop saves ~2.2M credits/month of the 21.9M total.
The filtering mechanism itself already shipped for PRs and has been running there; this extends it to the branch where it was never enabled.
Other
This is action 2 of the config-level cost work in the internal CI cost report (2026-08-31, "The CI Wedge"). No policy gate — the same correctness argument that justified PR filtering applies, and develop still runs every job whenever a shared path is touched (the catch-all, tracked separately in the catch-all narrowing issue, fires on 35.8% of merges).
Risk and mitigation: a misclassified change on develop skips tests a PR also skipped — the catch-all list errs broad by design, and the unrecognized-path guard in the script fails loudly on unmapped files.
Attn Agents:
Mechanism. .circleci/scripts/generate-pipeline-parameters.sh:82-91 is the branch override: on develop, release/*, or update-v8-snapshot-cache-on-develop it calls emit_all_true and exits before any path inspection. The larger gap: the parameters are only consumed by the PR workflow (.circleci/src/pipeline/workflows/pull-request.yml), whose jobs call the halt-if-skipped command (.circleci/src/pipeline/@pipeline.yml:1754-1766) guarded by run-* pipeline parameters. The develop pipeline runs the workflows in .circleci/src/pipeline/workflows/@main.yml, whose jobs consult no parameters at all. So this is two changes: (1) drop develop from the override in the script (keep release/* and the v8-snapshot branch); (2) thread the run-* guards into the @main.yml linux-x64 jobs — at minimum the GUI block (run-app-*, run-launchpad-*, run-reporter-*, run-frontend-shared-*, dev-server integration jobs) and the per-package npm jobs, since that is where the measured savings live.
Evidence. 400-merge replay (2026-06-03 → 2026-09-02), faithful reimplementation of the script including bash-glob semantics: engine-only 66.0%, docs/misc 14.5%, mixed 7.2%, npm-only 6.5%, GUI-only 5.8%. GUI block = 44.2% of a full pipeline's credits. Savings ~2.2M credits/mo (measured change mix × measured job costs); this replaced an earlier 40/30/30 assumption that priced the action at ~1.5M. Note 35.8% of develop merges trip the global catch-all and will still run everything until the catch-all narrowing lands — the 2.2M figure already accounts for that.
Fix design. Scheduled-run exemption first: pass TRIGGER_SOURCE: << pipeline.trigger_source >> into the script's environment in launch-primary-workflow (.circleci/config.yml:180-186, alongside RUN_ALL_JOBS) and emit_all_true when it equals scheduled_pipeline — before any branch or path logic. Belt-and-braces: also treat RUN_WINDOWS_WORKFLOW=true (and the platform-schedule parameter once the cron-migration issue adds it) as all-true, so a manually re-triggered nightly behaves like the scheduled one. For job selection, use halt-if-skipped in @main.yml jobs rather than omitting jobs from the graph — fan-in jobs (percy-finalize at @main.yml:34-47, ready-to-release, and the binary chain) hard-require long job lists, and a halted job still reports success so those lists keep working unmodified. Parameters are already inherited by the continued pipeline (see the comment at generate-pipeline-parameters.sh:43-47 for the collision gotcha with re-passing declared parameters). The script's develop branch already fetches upstream develop for the merge base; for a develop push, the merge base of HEAD vs develop is HEAD itself — the diff must be computed against the previous develop commit (HEAD~1 on a push, or << pipeline.git.base_revision >>), not the merge-base path used for PRs. Handle that branch explicitly.
Constraints. (1) create-and-trigger-packaging-artifacts, get-published-artifacts, the test-binary-* jobs, and npm-release must keep running on every develop merge until the release-only-signing policy issue decides otherwise — do not guard them. (2) emit_all_true must remain the behavior for release/*, electron/* (which never reach the script's override but run via @main.yml filters), update-v8-snapshot-cache-on-develop, the run-all-jobs manual trigger, and every scheduled pipeline — the nightly cron runs on the develop branch, so branch checks alone cannot distinguish it; the trigger_source check is load-bearing, not defensive. A scheduled run that gets path-filtered would silently gut the nightly platform sweep the cron-migration issue depends on. (3) The unrecognized-path guard (generate-pipeline-parameters.sh:350-356) exits 1 on unmapped files; on develop this would fail the merge pipeline rather than a PR — decide whether develop should fall back to emit_all_true on unrecognized paths instead of failing (recommended: fall back loudly, since the merge already passed PR CI).
Fallback. If threading parameters through @main.yml proves too invasive in one pass, gate only the GUI block first — it is 44.2% of pipeline credits and the engine-only 66% of merges skip all of it, capturing most of the 2.2M. The npm-job guards can follow.
Verification plan. (a) Replay harness: run the modified script against the same 400 merges and confirm classification is unchanged for PRs and now emits filtered params for develop. (b) Land, then push an engine-only merge and confirm GUI jobs halt in seconds while percy-finalize/ready-to-release still complete. (c) Push a GUI-only merge and confirm the GUI block runs. (d) Confirm npm-release and the binary chain ran on both. (e) Trigger the nightly schedule manually and confirm it emits all-true parameters and runs the full graph — this check is mandatory before merge. (f) Push to a release/* branch and confirm all-true. (g) After 2 weeks, compare develop-branch credits in Insights against baseline.
Interim mitigation. None needed. If a filtered develop run is ever suspect, Trigger Pipeline with run-all-jobs=true reruns everything (.circleci/config.yml:22-26).
What would you like?
Apply the existing PR path-filtering mechanism to pushes on
develop. Todaygenerate-pipeline-parameters.shcomputes which test groups a change actually needs, but a branch override forces every develop merge to run everything. Remove develop from that override and let the measured change decide, exactly as it does on PRs.Three cases keep running everything, explicitly:
release/*branches (andelectron/*, andupdate-v8-snapshot-cache-on-develop) — unchanged.run-all-jobs=truetriggers — unchanged.Why is this needed?
Develop merges are the second-biggest source of unfiltered spend, and we now have measured data on what they contain. A faithful replay of the CI path classifier over the last 400 first-parent merges to develop (2026-06-03 → 2026-09-02) found:
Two-thirds of develop merges could skip the GUI test block, which is 44.2% of a full pipeline's credits. Docs-only merges could skip nearly everything. Measured against job-level costs, filtering develop saves ~2.2M credits/month of the 21.9M total.
The filtering mechanism itself already shipped for PRs and has been running there; this extends it to the branch where it was never enabled.
Other
This is action 2 of the config-level cost work in the internal CI cost report (2026-08-31, "The CI Wedge"). No policy gate — the same correctness argument that justified PR filtering applies, and develop still runs every job whenever a shared path is touched (the catch-all, tracked separately in the catch-all narrowing issue, fires on 35.8% of merges).
Risk and mitigation: a misclassified change on develop skips tests a PR also skipped — the catch-all list errs broad by design, and the unrecognized-path guard in the script fails loudly on unmapped files.
Attn Agents:
Mechanism.
.circleci/scripts/generate-pipeline-parameters.sh:82-91is the branch override: ondevelop,release/*, orupdate-v8-snapshot-cache-on-developit callsemit_all_trueand exits before any path inspection. The larger gap: the parameters are only consumed by the PR workflow (.circleci/src/pipeline/workflows/pull-request.yml), whose jobs call thehalt-if-skippedcommand (.circleci/src/pipeline/@pipeline.yml:1754-1766) guarded byrun-*pipeline parameters. The develop pipeline runs the workflows in.circleci/src/pipeline/workflows/@main.yml, whose jobs consult no parameters at all. So this is two changes: (1) dropdevelopfrom the override in the script (keeprelease/*and the v8-snapshot branch); (2) thread therun-*guards into the@main.ymllinux-x64 jobs — at minimum the GUI block (run-app-*,run-launchpad-*,run-reporter-*,run-frontend-shared-*, dev-server integration jobs) and the per-package npm jobs, since that is where the measured savings live.Evidence. 400-merge replay (2026-06-03 → 2026-09-02), faithful reimplementation of the script including bash-glob semantics: engine-only 66.0%, docs/misc 14.5%, mixed 7.2%, npm-only 6.5%, GUI-only 5.8%. GUI block = 44.2% of a full pipeline's credits. Savings ~2.2M credits/mo (measured change mix × measured job costs); this replaced an earlier 40/30/30 assumption that priced the action at ~1.5M. Note 35.8% of develop merges trip the global catch-all and will still run everything until the catch-all narrowing lands — the 2.2M figure already accounts for that.
Fix design. Scheduled-run exemption first: pass
TRIGGER_SOURCE: << pipeline.trigger_source >>into the script's environment inlaunch-primary-workflow(.circleci/config.yml:180-186, alongsideRUN_ALL_JOBS) andemit_all_truewhen it equalsscheduled_pipeline— before any branch or path logic. Belt-and-braces: also treatRUN_WINDOWS_WORKFLOW=true(and the platform-schedule parameter once the cron-migration issue adds it) as all-true, so a manually re-triggered nightly behaves like the scheduled one. For job selection, usehalt-if-skippedin@main.ymljobs rather than omitting jobs from the graph — fan-in jobs (percy-finalizeat@main.yml:34-47,ready-to-release, and the binary chain) hard-require long job lists, and a halted job still reports success so those lists keep working unmodified. Parameters are already inherited by the continued pipeline (see the comment atgenerate-pipeline-parameters.sh:43-47for the collision gotcha with re-passing declared parameters). The script's develop branch already fetches upstream develop for the merge base; for a develop push, the merge base of HEAD vs develop is HEAD itself — the diff must be computed against the previous develop commit (HEAD~1on a push, or<< pipeline.git.base_revision >>), not the merge-base path used for PRs. Handle that branch explicitly.Constraints. (1)
create-and-trigger-packaging-artifacts,get-published-artifacts, thetest-binary-*jobs, andnpm-releasemust keep running on every develop merge until the release-only-signing policy issue decides otherwise — do not guard them. (2)emit_all_truemust remain the behavior forrelease/*,electron/*(which never reach the script's override but run via@main.ymlfilters),update-v8-snapshot-cache-on-develop, therun-all-jobsmanual trigger, and every scheduled pipeline — the nightly cron runs on the develop branch, so branch checks alone cannot distinguish it; thetrigger_sourcecheck is load-bearing, not defensive. A scheduled run that gets path-filtered would silently gut the nightly platform sweep the cron-migration issue depends on. (3) The unrecognized-path guard (generate-pipeline-parameters.sh:350-356) exits 1 on unmapped files; on develop this would fail the merge pipeline rather than a PR — decide whether develop should fall back toemit_all_trueon unrecognized paths instead of failing (recommended: fall back loudly, since the merge already passed PR CI).Fallback. If threading parameters through
@main.ymlproves too invasive in one pass, gate only the GUI block first — it is 44.2% of pipeline credits and the engine-only 66% of merges skip all of it, capturing most of the 2.2M. The npm-job guards can follow.Verification plan. (a) Replay harness: run the modified script against the same 400 merges and confirm classification is unchanged for PRs and now emits filtered params for develop. (b) Land, then push an engine-only merge and confirm GUI jobs halt in seconds while
percy-finalize/ready-to-releasestill complete. (c) Push a GUI-only merge and confirm the GUI block runs. (d) Confirmnpm-releaseand the binary chain ran on both. (e) Trigger the nightly schedule manually and confirm it emits all-true parameters and runs the full graph — this check is mandatory before merge. (f) Push to arelease/*branch and confirm all-true. (g) After 2 weeks, compare develop-branch credits in Insights against baseline.Interim mitigation. None needed. If a filtered develop run is ever suspect, Trigger Pipeline with
run-all-jobs=truereruns everything (.circleci/config.yml:22-26).