Skip to content

Commit eff75e5

Browse files
ci: concurrency cancel-in-progress + timeout-minutes on 4 long-running workflows (#115)
## Summary Resolves queue saturation. With proven on GitHub Pro (40 concurrent-job ceiling), every push to a feature branch was spawning a fresh Idris2 build (~20 min) and Architecture Enforcement run **without superseding the predecessor**. A 6-PR proof-debt session in ~90 min accumulated 32 in-progress + 36 queued jobs — blocking every other workflow. ## Change Adds to 4 long-running workflows: | Workflow | Concurrency | Per-job timeouts | |---|---|---| | architecture-enforcement.yml | + | enforce-idris-only: 5m, verify-idris-build: 20m | | idris2-ci.yml | + | build: 45m (cold-cache bootstrap), docs: 30m | | zig-ffi.yml | + | build-cross: 15m, build-macos: 20m, wasm-browser: 15m, release: 10m | | trustfile.yml | + | validate-trustfile: 5m, enforce-policies: 5m | Each subsequent push to the same ref now cancels its predecessor. Stuck jobs self-terminate at the declared budget instead of holding a slot for the 6-hour default. ## Bonus Closes the underlying `workflow_audit/missing_timeout_minutes` findings (currently mislabeled as `workflow_audit/unknown` in the Security tab; the label fix is hyperpolymath/hypatia#395) for these 4 workflows. ## Scope-out (deliberate) Remaining workflows tracked as follow-up: - `boj-build.yml`, `dogfood-gate.yml` — documented persistent-red non-blocking jobs (commit 7260383) - `casket-pages.yml`, `mirror.yml`, `instant-sync.yml`, `scorecard.yml`, `spark-theatre-gate.yml` — reusable wrappers (short) - `publish-*.yml`, `release.yml`, `cflite_*.yml` — rarely run ## Test plan - [ ] CI green - [ ] Push a fresh commit to this branch — verify the predecessor run on this branch gets cancelled (concurrency working) 🤖 Generated with [Claude Code](https://claude.com/claude-code) --------- Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1 parent d47824a commit eff75e5

5 files changed

Lines changed: 51 additions & 6 deletions

File tree

.github/workflows/architecture-enforcement.yml

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,18 @@ on:
1010
permissions:
1111
contents: read
1212

13+
# Cancel superseded runs on the same ref so a rapid sequence of pushes
14+
# to a feature branch does not pile up jobs against the Pro-plan 40-job
15+
# concurrent ceiling. Each new push to a ref supersedes its predecessor.
16+
concurrency:
17+
group: ${{ github.workflow }}-${{ github.ref }}
18+
cancel-in-progress: true
19+
1320
jobs:
1421
enforce-idris-only:
1522
runs-on: ubuntu-latest
23+
# Fast grep checks; if this exceeds 5 minutes something is wrong.
24+
timeout-minutes: 5
1625
steps:
1726
- uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4
1827

@@ -98,6 +107,10 @@ jobs:
98107

99108
verify-idris-build:
100109
runs-on: ubuntu-latest
110+
# Docker pull (snazzybucket/idris2) + Idris2 totality check across all
111+
# Proven.Safe.* modules. Local oracle runs in ~8 minutes; 20-minute
112+
# budget covers cold cache and the occasional Docker Hub slowdown.
113+
timeout-minutes: 20
101114
steps:
102115
- uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4
103116

.github/workflows/idris2-ci.yml

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,9 +23,20 @@ on:
2323
- '.github/workflows/idris2-ci.yml'
2424
workflow_dispatch:
2525

26+
# Cancel superseded runs on the same ref. Idris2 bootstrap + build is
27+
# ~20 minutes per run; without cancel-in-progress, a rapid PR-push
28+
# sequence saturates the Pro-plan 40-job concurrent ceiling within
29+
# minutes and blocks every other workflow estate-wide.
30+
concurrency:
31+
group: ${{ github.workflow }}-${{ github.ref }}
32+
cancel-in-progress: true
33+
2634
jobs:
2735
build:
2836
runs-on: ubuntu-latest
37+
# Idris2 bootstrap (cold cache) is ~10 min, build+typecheck ~12 min.
38+
# 45-minute budget gives cold-cache + slow apt mirror headroom.
39+
timeout-minutes: 45
2940
strategy:
3041
matrix:
3142
idris2-version: ['0.8.0']
@@ -87,7 +98,8 @@ jobs:
8798
runs-on: ubuntu-latest
8899
needs: build
89100
if: github.ref == 'refs/heads/main'
90-
101+
# Reuses Idris2 cache from build job; docs generation is fast (~5 min).
102+
timeout-minutes: 30
91103
steps:
92104
- name: Checkout
93105
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4

.github/workflows/trustfile.yml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,10 +18,15 @@ on:
1818
permissions:
1919
contents: read
2020

21+
concurrency:
22+
group: ${{ github.workflow }}-${{ github.ref }}
23+
cancel-in-progress: true
24+
2125
jobs:
2226
validate-trustfile:
2327
name: Validate Trustfile Integrity
2428
runs-on: ubuntu-latest
29+
timeout-minutes: 5
2530

2631
steps:
2732
- name: Checkout repository
@@ -84,6 +89,7 @@ jobs:
8489
name: Enforce Trustfile Policies
8590
runs-on: ubuntu-latest
8691
needs: validate-trustfile
92+
timeout-minutes: 5
8793

8894
steps:
8995
- name: Checkout repository

.github/workflows/zig-ffi.yml

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,10 +17,15 @@ on:
1717
- '.github/workflows/zig-ffi.yml'
1818
workflow_dispatch:
1919

20+
concurrency:
21+
group: ${{ github.workflow }}-${{ github.ref }}
22+
cancel-in-progress: true
23+
2024
jobs:
2125
# Cross-compile from Ubuntu (most targets)
2226
build-cross:
2327
runs-on: ubuntu-latest
28+
timeout-minutes: 15
2429
strategy:
2530
fail-fast: false
2631
matrix:
@@ -81,6 +86,7 @@ jobs:
8186
target: aarch64-macos
8287

8388
runs-on: ${{ matrix.os }}
89+
timeout-minutes: 20
8490

8591
steps:
8692
- name: Checkout proven
@@ -115,6 +121,7 @@ jobs:
115121

116122
wasm-browser:
117123
runs-on: ubuntu-latest
124+
timeout-minutes: 15
118125

119126
steps:
120127
- name: Checkout proven
@@ -147,6 +154,7 @@ jobs:
147154
runs-on: ubuntu-latest
148155
needs: [build-cross, build-macos, wasm-browser]
149156
if: github.ref == 'refs/heads/main' && github.event_name == 'push'
157+
timeout-minutes: 10
150158

151159
steps:
152160
- name: Download all artifacts

src/Proven/SafeBitset/Proofs.idr

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -83,12 +83,18 @@ fromIntegerCapacity cap n = Refl
8383
-- `singleton` bounds-rejection
8484
--------------------------------------------------------------------------------
8585

86-
||| OWED: `singleton 0 bit` always rejects (no bit fits in zero
87-
||| capacity). Blocked because `bit < 0` for variable `bit : Nat`
88-
||| does not Refl-reduce (standards#128 Nat-comparison opacity).
89-
public export
90-
0 singletonZeroCapRejects :
86+
||| DISCHARGED via 2-arm case-split on `bit`. The `bit < 0` check
87+
||| inside `singleton 0 bit` reduces on each constructor:
88+
||| * `Z < 0` → `compareNat 0 0 == LT` → `EQ == LT` → `False`
89+
||| * `S _ < 0` → `compareNat (S _) 0 == LT` → `GT == LT` → `False`
90+
||| Both make the `if` take the `else` branch, returning `Nothing`.
91+
||| The OWED's "abstract bit doesn't Refl-reduce" was correct for the
92+
||| zero-arg version; the case-split unblocks it.
93+
public export
94+
singletonZeroCapRejects :
9195
(bit : Nat) -> singleton 0 bit = Nothing
96+
singletonZeroCapRejects Z = Refl
97+
singletonZeroCapRejects (S _) = Refl
9298

9399
--------------------------------------------------------------------------------
94100
-- Capacity preservation on `Right`-branch (structural case split on `if`)

0 commit comments

Comments
 (0)