From 614dce1ec2fe0a242a6f8cfa3201118500d512b0 Mon Sep 17 00:00:00 2001 From: "SYM.BOT" Date: Thu, 30 Jul 2026 20:56:06 +0100 Subject: [PATCH] Make a green CI signal mean the tests ran MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Three defects, found while trying to decide whether a Node 18 hang belonged to PR #38 or to its base. None of them is exotic; together they mean this repo's green signal has not been evidence for some time. A PR TARGETING A STACKED BASE RAN NO CHECKS AT ALL. The pull_request trigger was filtered to `main`, so PR #39 — targeting the cutover branch feat/cmb-only-pin-core — ran zero jobs, while `gh pr view` reported mergeStateStatus CLEAN and `gh pr checks` said "no checks reported". At the surface a reviewer actually looks at, unverified and passing are the same colour. The filter is removed: every branch that opens a PR is checked. Stacked work is exactly the work most worth checking, because its base is not main. A HANG RECORDED ITSELF AS "cancelled", NOT "failed". The Node 18 matrix job has run past six hours twice while Node 22 finished in ~30s. With no job timeout it burns to the platform ceiling; with cancel-in-progress unconditional, any later run supersedes it first. So a persistent, reproducible break left a trail of cancelled runs that reads as CI noise. timeout-minutes: 15 makes a hang fail fast and red, and cancel-in-progress is now conditional: superseding a PR push is fine, superseding `main` destroys the only record of whether the mainline is green. fail-fast: false, so a Node 18 hang can no longer cancel the Node 22 job and take the one working signal down with it. workflow_dispatch, so `main` can be run on demand. Isolating "is this break mine or the base's?" previously required pushing to main — precisely when you least want to. NOT FIXED HERE, AND DELIBERATELY SO: whatever makes Node 18 hang. engines declares node >=18 and the matrix tests it, so dropping it would be quietly narrowing a supported range to make a board go green. This change makes the failure visible and diagnosable in fifteen minutes instead of invisible for six hours; the diagnosis is its own piece of work, and it needs a Node 18 environment to reproduce. The first run of this workflow is expected to go RED on Node 18. That is the point. Co-Authored-By: Claude Opus 5 (1M context) Claude-Session: https://claude.ai/code/session_01SpDHL4kTRWvkuw63KADT6R --- .github/workflows/ci.yml | 22 ++++++++++++++++++++-- 1 file changed, 20 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 55e379b..06b5030 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -3,17 +3,35 @@ name: CI on: push: branches: [main] + # NOT filtered to `main`. A PR targeting a stacked base (e.g. a cutover branch) + # used to run NO checks at all, while `gh pr view` still reported the merge state + # as CLEAN — indistinguishable, at the surface anyone actually looks at, from a PR + # whose tests passed. Every branch that opens a PR gets checked. pull_request: - branches: [main] + # So `main` can be run on demand. Isolating "is this break mine or the base's?" + # otherwise required pushing to main, which is exactly when you least want to. + workflow_dispatch: concurrency: group: ci-${{ github.ref }} - cancel-in-progress: true + # Superseding a PR push is fine — nobody needs the older run. Superseding `main` + # is not: it destroys the only record of whether the mainline is green, and a job + # that hangs long enough is ALWAYS superseded, so a persistent failure records + # itself as a string of "cancelled" runs and reads as noise rather than breakage. + cancel-in-progress: ${{ github.ref != 'refs/heads/main' }} jobs: test: runs-on: ubuntu-latest + # A hang must fail fast and loudly. Without this a stuck job burns to the 6h + # platform ceiling and then reports "cancelled" — the failure mode that hid the + # Node 18 break: Node 22 passed in ~30s while Node 18 ran for six hours, twice, + # and neither run left a red mark behind. + timeout-minutes: 15 strategy: + # Report every version's real result. fail-fast would let a Node 18 hang cancel + # the Node 22 job and lose the one signal that still works. + fail-fast: false matrix: node-version: [18, 22] steps: