diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 3c1bc59..4dedf45 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -9,10 +9,39 @@ env: CARGO_TERM_COLOR: always RUSTFLAGS: "-D warnings" +# Cancel a pull request's superseded runs instead of letting them burn +# to completion. This project rebases heavily — the ledger re-conflicts +# on nearly every merge — so a branch routinely takes several pushes +# while an earlier run is still going, and each of those runs is +# obsolete the moment the next push lands. macOS minutes are the +# expensive ones and the macOS leg is the critical path, so superseded +# runs are exactly where the waste concentrates. +# +# Scoped to pull requests deliberately. `github.event.pull_request.number` +# is empty for a push to `main`, so the fallback keys those runs by SHA: +# every `main` commit gets its own group and none can cancel another. +# Cancelling a `main` run would leave the branch-protection record +# ambiguous about a commit that has already landed — the one place this +# saving is not worth having. +concurrency: + group: ci-${{ github.event.pull_request.number || github.sha }} + cancel-in-progress: ${{ github.event_name == 'pull_request' }} + +# Every job carries `timeout-minutes`. Without one a job inherits +# GitHub's 360-minute default, so a single hung test burns six hours — +# times four on the test matrix — and reports nothing useful at the end +# of it. The measured critical path is ~14.6 min (macOS/luajit), so 25 +# leaves ample headroom for a slow runner while catching a hang in +# under half an hour. `m6-perf-gates` keeps its tighter 15. +# +# This is also the gate that has to exist before the basedpyright-class +# hang can ever be armed — see `PMACS_REQUIRE_PYRIGHT`, deliberately +# never set, in the test job below. jobs: fmt: name: Format runs-on: ubuntu-latest + timeout-minutes: 25 steps: - uses: actions/checkout@v4 - uses: dtolnay/rust-toolchain@stable @@ -23,6 +52,7 @@ jobs: clippy: name: Lint (${{ matrix.lua }}) runs-on: ubuntu-latest + timeout-minutes: 25 strategy: fail-fast: false matrix: @@ -40,10 +70,17 @@ jobs: # not pmacs), so the root-package clippy above never lints it. Lint # it explicitly or its warnings slip through CI (audit F-001). - run: cargo clippy -p pmacs-gpu --all-targets -- -D warnings + # pmacs-protocol is likewise never linted by the root-package + # clippy above: the workspace default member is only `pmacs`. The + # local `--workspace` gate covers it, so it passes today — CI has + # simply never checked, and a warning introduced through a + # protocol-only PR would reach `main` unseen. + - run: cargo clippy -p pmacs-protocol --all-targets -- -D warnings gpu-render: name: GPU Render (headless) runs-on: ubuntu-latest + timeout-minutes: 25 steps: - uses: actions/checkout@v4 - uses: dtolnay/rust-toolchain@stable @@ -76,6 +113,7 @@ jobs: test: name: Test (${{ matrix.os }} / ${{ matrix.lua }}) runs-on: ${{ matrix.os }} + timeout-minutes: 25 strategy: fail-fast: false matrix: @@ -160,6 +198,7 @@ jobs: acceptance: name: M1 Acceptance Gates runs-on: ubuntu-latest + timeout-minutes: 25 steps: - uses: actions/checkout@v4 - uses: dtolnay/rust-toolchain@stable @@ -170,6 +209,7 @@ jobs: m4-perf-gates: name: M4 Perf Gates runs-on: ubuntu-latest + timeout-minutes: 25 steps: - uses: actions/checkout@v4 - uses: dtolnay/rust-toolchain@stable @@ -188,6 +228,7 @@ jobs: m5-perf-gates: name: M5 Perf Gates runs-on: ubuntu-latest + timeout-minutes: 25 steps: - uses: actions/checkout@v4 - uses: dtolnay/rust-toolchain@stable diff --git a/docs/active-work.md b/docs/active-work.md index dd0016a..3b283d4 100644 --- a/docs/active-work.md +++ b/docs/active-work.md @@ -820,6 +820,45 @@ has **no branch and no framing yet**. current documentation. The section said "whoever confirms the branch carries nothing unique removes the section"; this is that. +## Test-improvement arc, lane 3a — CI timeouts and concurrency + +- Portable branch: `githubsucks/ci-timeouts-concurrency`, worktree + `../pmacs-ci3`. Workflow only — **no product code, no tests changed.** +- **Base, measured at write time:** + + ``` + $ git log --oneline -1 githubsucks/main + b7bf2c6 Merge pull request #194 from levineuwirth/silent-skip-arming + ``` + +- Ships the three cheap, deterministic items of `TEST_IMPROVEMENT.md` + §5-6. The larger ones — nextest (§6.3), the serial/parallel split + (§6.2), the parallel canary leg (§5.6), the nightly cron (§5.5), and + the macOS matrix trim (§6.4) — are **deliberately not here**: each + changes what CI certifies or how it runs, and each wants its own + decision rather than riding a timeout patch. +- **`timeout-minutes` on every job (§5.2).** Measured before changing: + **7 of 8 jobs had none** and inherited GitHub's 360-minute default; + only `m6-perf-gates` had one (15). A single hung test therefore burnt + six hours, times four on the test matrix. Set to 25 against a + measured ~14.6 min critical path (macOS/luajit). + **This is the gate that must land before `PMACS_REQUIRE_PYRIGHT` can + ever be set** — lane 2 left basedpyright unarmed precisely because + this did not exist. +- **`concurrency` with `cancel-in-progress` (§6.1)**, scoped to pull + requests. `github.event.pull_request.number` is empty on a push to + `main`, so the fallback keys those by SHA and no `main` run can + cancel another — cancelling one would leave the branch-protection + record ambiguous about a commit that already landed. +- **`-p pmacs-protocol` clippy (§5.7).** Verified passing locally + *before* proposing it, so adding it cannot turn CI red on arrival. + The root-package clippy never covered it: the workspace default + member is only `pmacs`. +- Recovery from a clean checkout: + `git fetch githubsucks && git worktree add ../pmacs-ci3 + -b ci-timeouts-concurrency githubsucks/ci-timeouts-concurrency`. + + ## Parked lane: kill-ring browser + persistence - Portable branch: `githubsucks/kill-ring-browser`