diff --git a/.changeset/dogfood-gate-cancelled-not-failure.md b/.changeset/dogfood-gate-cancelled-not-failure.md new file mode 100644 index 0000000000..5cc4c58a51 --- /dev/null +++ b/.changeset/dogfood-gate-cancelled-not-failure.md @@ -0,0 +1,4 @@ +--- +--- + +CI-only: the Dogfood Regression Gate no longer reports a superseded (cancelled) run as a failure — `cancelled` joins the pass branch, backed by a fail-fast experiment showing a real shard failure always aggregates as `failure`. Releases nothing. diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 6019a739a6..4102eaf029 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -286,8 +286,21 @@ jobs: run: | result="${{ needs.dogfood.result }}" echo "dogfood matrix aggregate result: $result" + # cancelled is a run-lifecycle state, not a shard verdict (#3668): + # with cancel-in-progress on, every superseded push cancelled the + # in-flight dogfood matrix — the longest job, so almost always the + # one still running — and the old `*)` fallthrough painted a false + # red on the old SHA. Verified experimentally (run 30271824408, a + # fail-fast matrix with one real failure + one cancelled sibling): + # a real shard failure DOMINATES the aggregate — it reads "failure", + # never "cancelled" — so "cancelled" here can only mean the whole + # run was stopped from outside (supersession, or a manual cancel — + # accepted trade-off), and passing it masks no regression. + # Deliberately NOT `if: !cancelled()` on the job instead: a skipped + # gate publishes no required-check context on the SHA, which is the + # #3622 merge-deadlock all over again. case "$result" in - success|skipped) echo "Dogfood gate satisfied." ;; + success|skipped|cancelled) echo "Dogfood gate satisfied ($result)." ;; *) echo "::error::Dogfood shards did not pass (aggregate result: $result)"; exit 1 ;; esac