Skip to content

Commit 11b5e8a

Browse files
hyperpolymathclaude
andcommitted
ci: wire the formal/ Coq proof gate into CI (Refs #513)
`formal/` holds 20 machine-checked Coq/Rocq proofs and `formal/justfile` already implements a real, fail-closed check over them — but no workflow ever invoked it, so the corpus was unchecked in CI. Verified locally with coqc 8.20.1 before wiring: all 20 files check clean, every `Print Assumptions` reports "Closed under the global context", and the justfile's own assertion fires ("no axioms"). Deprecation warnings for `app_length` only (8.18 -> 8.20 skew), no errors. The gate is deliberately fail-closed: it does not probe for the prover and skip when absent. The pinned container guarantees `coqc`, so a missing prover is an infrastructure failure rather than a silent pass. `formal/justfile` stays the single source of truth for the proof list and its dependency order; the workflow parses that list instead of duplicating it, and fails if any `formal/*.v` on disk is not named there — so adding a proof without wiring it cannot go unnoticed. Both guards were falsifier-tested locally: - an unwired `formal/*.v` is detected and fails the run - an `Axiom`-dependent theorem is caught by the "Axioms:" grep Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
1 parent 1c169c0 commit 11b5e8a

1 file changed

Lines changed: 96 additions & 0 deletions

File tree

Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
# SPDX-License-Identifier: MPL-2.0
2+
# Coq/Rocq proof gate for the `formal/` mechanised-metatheory track (issue #513).
3+
#
4+
# This gate is deliberately FAIL-CLOSED. It does NOT probe for the prover and
5+
# skip when absent — the container guarantees `coqc` exists, so a missing
6+
# prover is an infrastructure failure, not a silent pass.
7+
#
8+
# `formal/justfile` remains the single source of truth for the proof list and
9+
# its dependency order; this workflow parses that list rather than duplicating
10+
# it, and fails if any `formal/*.v` on disk is not named there.
11+
name: Coq Proof Gate
12+
on:
13+
pull_request:
14+
paths:
15+
- 'formal/**'
16+
- '.github/workflows/coq-proof-gate.yml'
17+
push:
18+
branches: [main]
19+
paths:
20+
- 'formal/**'
21+
- '.github/workflows/coq-proof-gate.yml'
22+
workflow_dispatch:
23+
permissions: read-all
24+
concurrency:
25+
group: ${{ github.workflow }}-${{ github.ref }}
26+
cancel-in-progress: true
27+
jobs:
28+
coq-proofs:
29+
runs-on: ubuntu-latest
30+
timeout-minutes: 30
31+
container:
32+
# coqorg/coq:8.20 — pinned by digest. `formal/README.adoc` documents 8.18;
33+
# the corpus was verified to check clean on 8.20.1 (deprecation warnings
34+
# for `app_length` only, no errors).
35+
image: coqorg/coq@sha256:e50d77c4c5a9aa0d76ae1b343d79c5f922da3a75054b79c5dc635895438e4674
36+
options: --user root
37+
steps:
38+
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
39+
40+
- name: Record prover version
41+
run: coqc --version
42+
43+
- name: Extract the ordered proof list from formal/justfile
44+
id: list
45+
run: |
46+
set -euo pipefail
47+
cd formal
48+
list="$(sed -n '/for f in /,/; do/p' justfile \
49+
| tr '\n' ' ' \
50+
| sed 's/.*for f in //; s/; do.*//; s/\\//g' \
51+
| tr -s ' ')"
52+
if [ -z "${list// /}" ]; then
53+
echo "::error::could not parse the proof list out of formal/justfile"
54+
exit 1
55+
fi
56+
echo "count=$(printf '%s' "$list" | wc -w)"
57+
echo "list=$list" >> "$GITHUB_OUTPUT"
58+
59+
- name: Guard — every formal/*.v must be wired into the gate
60+
env:
61+
LIST: ${{ steps.list.outputs.list }}
62+
run: |
63+
set -euo pipefail
64+
cd formal
65+
missing=0
66+
for f in *.v; do
67+
b="${f%.v}"
68+
case " $LIST " in
69+
*" $b "*) ;;
70+
*) echo "::error file=formal/$f::proof file is not listed in formal/justfile — it would never be checked"; missing=1 ;;
71+
esac
72+
done
73+
[ "$missing" -eq 0 ] || exit 1
74+
echo "all on-disk proofs are wired into the gate"
75+
76+
- name: Type-check every proof and reject any axiom / Admitted
77+
env:
78+
LIST: ${{ steps.list.outputs.list }}
79+
run: |
80+
set -euo pipefail
81+
cd formal
82+
all=""
83+
for f in $LIST; do
84+
echo "== coqc $f.v =="
85+
o="$(coqc -Q . ASFormal "$f.v")"
86+
printf '%s\n' "$o"
87+
all+="$o"$'\n'
88+
done
89+
# `Print Assumptions` emits "Axioms:" when a theorem depends on an
90+
# axiom or an `Admitted` proof; "Closed under the global context"
91+
# is the clean result.
92+
if printf '%s' "$all" | grep -q "Axioms:"; then
93+
echo "::error::a proof depends on an axiom / Admitted"
94+
exit 1
95+
fi
96+
echo "OK: all proofs mechanised; no axioms."

0 commit comments

Comments
 (0)