Skip to content

Commit abc58b1

Browse files
feat(coq-build): always-on aggregator gate (closes #244 path-filter blocker) (#254)
## Summary Closes the path-filter blocker that prevents #244 (the owner action that adds `required_status_checks` to the Base ruleset on `main`) from landing without collateral damage. The `Coq build — formal/_CoqProject` check shipped in #231 is path-filtered to `formal/**` + the workflow file itself. Once the Base ruleset (id `14285235`) gains a `required_status_checks` rule naming that context, every PR that does NOT touch `formal/**` would leave the check "expected but not reported" and be blocked indefinitely. Fix: refactor to the always-on aggregator pattern. The single new context that goes into the ruleset is `coq-build-gate`; it ALWAYS runs and reports the right verdict regardless of which paths changed. ## Mechanics 1. `on:` block drops the `paths:` filter — workflow triggers on every PR and every push to main. 2. New `detect-relevant-changes` job diffs against the PR base (`github.event.pull_request.base.sha` / `github.event.before`) and sets `outputs.relevant = true|false` using the same path list that lived in the on-trigger (`formal/` + this workflow). 3. The existing `coq-build` job gains `needs: detect-relevant-changes` + `if: needs.detect-relevant-changes.outputs.relevant == 'true'`. It skips cleanly on irrelevant PRs. 4. New `coq-build-gate` job (`if: always()`, `needs: [detect, coq-build]`): - SUCCESS immediately if `RELEVANT != 'true'`. - FAILURE if `detect-relevant-changes` itself didn't succeed (fail-safe). - SUCCESS iff `coq-build.result == 'success'` on a relevant change. - FAILURE otherwise. The `coq-build` job itself is untouched: same `coqorg/coq:8.18` container with `--user root`, same `coq_makefile -f _CoqProject -o build.mk && make`, same `Print Assumptions` audit of the four Phase D theorems (`preservation_l1`, `preservation_l2_via_l1`, `preservation_l2_app_eff_beta_linear`, `preservation_l2_app_eff_beta_ground_nonlinear`). ## Ruleset migration (post-merge, separate operation) Once this PR is green on main, the #244 owner action becomes: ```bash # Append required_status_checks naming `coq-build-gate` (NOT the underlying # `Coq build — formal/_CoqProject`, because that one skips on irrelevant PRs # and would re-introduce the exact "expected but not reported" block this # PR exists to avoid). gh api -X PUT repos/hyperpolymath/ephapax/rulesets/14285235 --input /tmp/patch.json ``` ## Test plan - [ ] `coq-build-gate` green on this PR (path is `.github/workflows/coq-build.yml` → relevant=true → coq-build runs → gate aggregates). - [ ] After merge, a no-formal-change PR shows `coq-build-gate` green while `coq-build` is "Skipped". - [ ] After merge, the #244 owner action switches the Base ruleset's required context from absent to `coq-build-gate`. - [ ] An intentional broken-build PR (touching `formal/`) shows `coq-build-gate` red and admin-merge blocked. ## Prior-art panic-attack#90 (already landed) applied the same pattern to `chapel-ci.yml`. This PR is the single-job variant of the same refactor. ## Refs - #244 — owner action this PR unblocks - #231 — `coq-build.yml` ships - #236 — EACCES container fix - #243 — Print Assumptions audit expansion - panic-attack#90 — prior-art (chapel-ci aggregator) Generated with [Claude Code](https://claude.com/claude-code)
1 parent 7c4c3f5 commit abc58b1

1 file changed

Lines changed: 84 additions & 6 deletions

File tree

.github/workflows/coq-build.yml

Lines changed: 84 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -18,19 +18,26 @@
1818
# results (preservation_l1 at L1; preservation_l2_via_l1 plus the two
1919
# β-case lemmas at L2) so any new `Admitted.` / `Axiom` slippage shows
2020
# up as a diff in the workflow output.
21+
#
22+
# **Why an aggregator gate?** The `coq-build` job is path-filtered: it
23+
# only does real work when `formal/**` or this workflow file changes.
24+
# But path-filtered workflows that don't trigger leave required status
25+
# checks "expected but not reported" — which would block unrelated PRs
26+
# from merging once this check is in the Base ruleset (#244). Solution:
27+
# a single `coq-build-gate` job that ALWAYS runs and aggregates. The
28+
# ruleset requires only the gate. The gate reports:
29+
# - SUCCESS immediately if no formal-relevant paths changed.
30+
# - SUCCESS if `coq-build` succeeded on a relevant change.
31+
# - FAILURE if `coq-build` failed.
32+
#
33+
# Prior-art (same pattern): panic-attack#90.
2134

2235
name: Coq Build (formal/)
2336

2437
on:
2538
pull_request:
26-
paths:
27-
- 'formal/**'
28-
- '.github/workflows/coq-build.yml'
2939
push:
3040
branches: [main]
31-
paths:
32-
- 'formal/**'
33-
- '.github/workflows/coq-build.yml'
3441

3542
permissions:
3643
contents: read
@@ -40,8 +47,42 @@ concurrency:
4047
cancel-in-progress: true
4148

4249
jobs:
50+
detect-relevant-changes:
51+
name: detect-relevant-changes
52+
runs-on: ubuntu-latest
53+
outputs:
54+
relevant: ${{ steps.f.outputs.relevant }}
55+
steps:
56+
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
57+
with:
58+
fetch-depth: 2
59+
- id: f
60+
name: Detect formal-relevant paths
61+
run: |
62+
set -euo pipefail
63+
BASE="${{ github.event.pull_request.base.sha || github.event.before }}"
64+
if [[ -z "$BASE" || "$BASE" == "0000000000000000000000000000000000000000" ]]; then
65+
# First push or detached state — be safe and run the full gate.
66+
echo "relevant=true" >> "$GITHUB_OUTPUT"
67+
echo "detect: BASE missing/zero — treating as relevant"
68+
exit 0
69+
fi
70+
git fetch origin "$BASE" --depth=1 2>/dev/null || true
71+
CHANGED=$(git diff --name-only "$BASE" HEAD || true)
72+
echo "Changed files:"
73+
echo "$CHANGED"
74+
if echo "$CHANGED" | grep -qE '^(formal/|\.github/workflows/coq-build\.yml$)'; then
75+
echo "relevant=true" >> "$GITHUB_OUTPUT"
76+
echo "detect: formal-relevant paths changed — running gate"
77+
else
78+
echo "relevant=false" >> "$GITHUB_OUTPUT"
79+
echo "detect: no formal-relevant paths — gate skipped via if-guard"
80+
fi
81+
4382
coq-build:
4483
name: Coq build — formal/_CoqProject
84+
needs: detect-relevant-changes
85+
if: needs.detect-relevant-changes.outputs.relevant == 'true'
4586
runs-on: ubuntu-latest
4687
# coq:8.18 matches the toolchain pinned by formal/Justfile and the
4788
# local developer setup. Pin to a digest later once a stable
@@ -98,3 +139,40 @@ jobs:
98139
Print Assumptions preservation_l2_app_eff_beta_linear.
99140
Print Assumptions preservation_l2_app_eff_beta_ground_nonlinear.
100141
EOF
142+
143+
# Always-on aggregator. This is the ONLY job listed in the Base ruleset's
144+
# required_status_checks rule (#244). If detect-relevant-changes determined
145+
# nothing in this PR touches formal-relevant paths, the gate passes
146+
# immediately (the underlying `coq-build` job skips via its `if:` guard).
147+
# If a relevant change is present, the gate inspects `coq-build.result` and
148+
# only passes when it returned `success`.
149+
coq-build-gate:
150+
name: coq-build-gate
151+
needs: [detect-relevant-changes, coq-build]
152+
if: always()
153+
runs-on: ubuntu-latest
154+
steps:
155+
- name: Aggregate coq-build results
156+
env:
157+
RELEVANT: ${{ needs.detect-relevant-changes.outputs.relevant }}
158+
R_DETECT: ${{ needs.detect-relevant-changes.result }}
159+
R_BUILD: ${{ needs.coq-build.result }}
160+
run: |
161+
set -euo pipefail
162+
echo "detect-relevant-changes.outputs.relevant=$RELEVANT"
163+
echo "detect-relevant-changes.result=$R_DETECT"
164+
echo "coq-build.result=$R_BUILD"
165+
if [[ "$RELEVANT" != "true" ]]; then
166+
echo "coq-build-gate: SKIP (no formal-relevant paths changed) → PASS"
167+
exit 0
168+
fi
169+
# If detect itself failed, we never confirmed relevance — fail safe.
170+
if [[ "$R_DETECT" != "success" ]]; then
171+
echo "coq-build-gate: detect-relevant-changes did not succeed → FAIL"
172+
exit 1
173+
fi
174+
if [[ "$R_BUILD" != "success" ]]; then
175+
echo "coq-build-gate: coq-build did not succeed (got '$R_BUILD') → FAIL"
176+
exit 1
177+
fi
178+
echo "coq-build-gate: coq-build green → PASS"

0 commit comments

Comments
 (0)