Skip to content

Commit 343867d

Browse files
fix(proofs): repair WokeLang.lean to verify under Lean 4.30.0 + add CI gate (#79)
## Summary The Lean 4 formal-verification file `docs/proofs/verification/WokeLang.lean` claimed *"all 12 `sorry` eliminated / verified"* — but it had **silently bit-rotted** and did **not compile** under a current Lean toolchain (~100 errors). Root cause: **no CI ever ran the prover** (workflows only covered the OCaml core + e2e), so the headline correctness claim was never machine-enforced. This PR restores **sorry-free compilation under Lean 4.30.0**, adds the missing **CI gate**, and documents a coverage/correspondence audit. ## What changed **Only proof bodies + 2 `deriving` clauses + 1 `abbrev`** — every theorem signature and every inductive is **byte-identical** (verified by diff). No statement was weakened; no `sorry`/`admit`/`axiom` introduced. Root causes fixed: - **`DecidableEq` no longer derives** for `Value`/`WokeType` (`Float` has none — NaN; and nested `List Self` blocks the deriving handler) → reduced to `deriving Repr`; equality decisions use Lean's classical `by_cases` (no instance needed). - **`induction`/`cases` on concrete indices** (`emptyTypeEnv`, `emptyEnv`) + binder-arity drift across Lean versions. `preservation` made **type-polymorphic** via `induction hs generalizing t` (its congruence cases need the IH at the operand type, not the result type). `cases … with | inr ⟨..⟩` anonymous-constructor patterns → `obtain` (no longer supported in `cases`). Type-mismatched reduction siblings discharged with `nomatch`. - **`BEq Permission`**: made `Permission` a reducible `abbrev` so the `String` instance transfers. ## Verification ``` $ lean docs/proofs/verification/WokeLang.lean # exit 0, no output ``` Machine-checked: `progress`, `preservation`, `type_safety` (multi-step), `consent_monotonicity`, `consent_preservation` — for the expression core (incl. the Result/`unwrap`/`error` panic-propagation fragment). ## Added - **`docs/proofs/verification/lean-toolchain`** — pins Lean v4.30.0. - **`docs/proofs/verification/AUDIT.md`** — answers PROOF-NEEDS.md #1 & #2: the proofs cover a **clean expression core** (a strict, honestly-scoped subset), the Lean type system corresponds to the **Rust** typechecker (not the OCaml `core/eval.ml`, which models a different language), and the consent theorems correspond to the spec's Hoare logic. - **`.github/workflows/lean-proofs.yml`** — installs the pinned toolchain and runs the check on every push to the proof dir, so this can't bit-rot again. SHA-pinned action, pure-bash install (matches estate governance). ## Note on scope This repairs and re-verifies the **existing** proofs and makes them durable. It does **not** yet extend coverage (more binops/comparisons, statements, arrays, compiler/VM correctness) — those remain per PROOF-NEEDS.md and are tabulated in AUDIT.md. https://claude.ai/code/session_013wg3Mtq2QFhYi4XVw1Z6z7 --- _Generated by [Claude Code](https://claude.ai/code/session_013wg3Mtq2QFhYi4XVw1Z6z7)_ Co-authored-by: Claude <noreply@anthropic.com>
1 parent f51b27e commit 343867d

4 files changed

Lines changed: 288 additions & 89 deletions

File tree

.github/workflows/lean-proofs.yml

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
# SPDX-License-Identifier: MPL-2.0
2+
# lean-proofs.yml — machine-checks the Lean 4 formal-verification proofs.
3+
#
4+
# Guards the root cause documented in docs/proofs/verification/AUDIT.md: the
5+
# proofs had bit-rotted because no CI ever ran the prover. This gate keeps the
6+
# "sorry-free, compiles" invariant honest from now on.
7+
name: lean-proofs
8+
9+
on:
10+
push:
11+
paths:
12+
- 'docs/proofs/verification/**'
13+
- '.github/workflows/lean-proofs.yml'
14+
pull_request:
15+
paths:
16+
- 'docs/proofs/verification/**'
17+
- '.github/workflows/lean-proofs.yml'
18+
19+
permissions:
20+
contents: read
21+
22+
jobs:
23+
lean-check:
24+
runs-on: ubuntu-latest
25+
steps:
26+
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
27+
28+
- name: Install Lean (version pinned by lean-toolchain)
29+
run: |
30+
set -euo pipefail
31+
ver="$(sed -E 's#.*:v##' docs/proofs/verification/lean-toolchain)"
32+
echo "Installing Lean ${ver}"
33+
sudo apt-get update
34+
sudo apt-get install -y zstd
35+
curl -sSL -o /tmp/lean.tar.zst \
36+
"https://github.com/leanprover/lean4/releases/download/v${ver}/lean-${ver}-linux.tar.zst"
37+
sudo mkdir -p /opt/lean
38+
sudo tar --use-compress-program=unzstd -xf /tmp/lean.tar.zst -C /opt/lean
39+
echo "/opt/lean/lean-${ver}-linux/bin" >> "$GITHUB_PATH"
40+
41+
- name: Verify WokeLang.lean (sorry-free; must exit 0 with no errors)
42+
run: |
43+
set -euo pipefail
44+
lean --version
45+
lean docs/proofs/verification/WokeLang.lean
46+
echo "✅ WokeLang.lean verified"

docs/proofs/verification/AUDIT.md

Lines changed: 121 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,121 @@
1+
<!--
2+
SPDX-License-Identifier: MPL-2.0
3+
SPDX-FileCopyrightText: 2026 Hyperpolymath
4+
-->
5+
# WokeLang Lean4 Proof Audit
6+
7+
This document records a completeness/correspondence audit of
8+
`WokeLang.lean`, answering the two questions raised in
9+
[`PROOF-NEEDS.md`](../../../PROOF-NEEDS.md): *is the sorry-free claim still
10+
true?* and *do the proofs cover the full type system or only a subset?*
11+
12+
## Toolchain
13+
14+
- **Prover:** Lean 4, pinned to **v4.30.0** (`lean-toolchain` in this
15+
directory).
16+
- **Build / check:** the proof file has **no external dependencies**
17+
(no Mathlib — only the Lean core prelude), so it is checked directly:
18+
19+
```sh
20+
lean docs/proofs/verification/WokeLang.lean # exit 0, no output ⇒ verified
21+
```
22+
23+
- **CI:** `.github/workflows/lean-proofs.yml` installs the pinned toolchain
24+
and runs the check on every push. See the headline finding below for why
25+
this gate was added.
26+
27+
## Headline finding — the proofs had bit-rotted (now fixed)
28+
29+
The file's doc-comment claimed *"all 12 `sorry` eliminated … verified"*. That
30+
claim was **true against an older Lean but had silently broken**: under Lean
31+
4.30.0 the file did **not** compile (~100 errors). Two root causes:
32+
33+
1. **`deriving DecidableEq` no longer applies** to `Value` and `WokeType`.
34+
`Value` contains `Float` (no `DecidableEq` in Lean 4 — NaN), and both types
35+
nest `List Self`, which the `DecidableEq` deriving handler does not see
36+
through. Fixed by deriving only `Repr`; equality decisions that the proofs
37+
need (`by_cases v₁ = v₂`) work via Lean's classical fallback with no
38+
instance required.
39+
2. **`induction h` on `HasType emptyTypeEnv …`** fails because `emptyTypeEnv`
40+
is a *concrete* (non-variable) index. Fixed by generalising the index
41+
(`generalize hΓ : emptyTypeEnv = Γ at h`) and threading the equation
42+
through the induction hypotheses.
43+
44+
The decisive lesson: **no CI ever ran the prover** (workflows only covered the
45+
OCaml core and e2e), so the headline correctness claim was never enforced.
46+
The repair restores `sorry`-free compilation *and* adds the missing CI gate.
47+
48+
> The numeric "12 `sorry`" figure in the file header is historical; the
49+
> verified invariant going forward is simply *the CI `lean` check is green*.
50+
51+
## Coverage — Lean model vs. the real language
52+
53+
The Lean model formalises a **clean expression core** and is a **strict
54+
subset** of the surface language defined in `core/ast.ml` and implemented by
55+
the Rust type checker (`src/typechecker/mod.rs`).
56+
57+
| Area | Lean `WokeLang.lean` | Surface language (`core/ast.ml`, `src/typechecker`) |
58+
|---|---|---|
59+
| Literals | int, float, string, bool, unit | + arrays, measured, `thanks` |
60+
| Arithmetic | `add` only (int/float/string) | `add sub mul div mod` (+ div/mod-by-zero), **mixed int/float promotion** |
61+
| Comparison | `eq` (same-type), no ordering | `eq ne lt gt le ge`, structural `eq` across **any** types |
62+
| Logical | `and` (both `bool`) | `and or` with **`to_bool` coercion** of any value |
63+
| Result type | `okay`/`oops`/`unwrap`/`error`, `tOkay…tUnwrap` | present in **Rust** typechecker (`Result(_, _)`); **absent** from `core/eval.ml` |
64+
| Unary | `neg` (int/float), `not` | + measured propagation |
65+
| Calls / arrays | `call`, `array` exprs (no typing rules) | builtins + user functions; arrays typed |
66+
| Statements | declared (`Stmt`), **no typing/exec** | full eval in `core/eval.ml` |
67+
| Units of measure | **not modelled** | `EMeasured` / `VMeasured`, unit-match checking |
68+
| Pattern matching, workers, custom types | **not modelled** | present |
69+
| Consent | `consent_monotonicity/preservation` | matches **spec** `axiomatic-semantics.md` (consent Hoare logic) |
70+
| Capabilities | data + `capSubsumes`/`hasCapability` (no theorems) ||
71+
72+
### What *is* proven (and now machine-checked)
73+
`canonical_forms_{int,float,string,bool,result}`, **`progress`**,
74+
**`preservation`**, **`type_safety`** (multi-step), and
75+
`consent_{monotonicity,preservation}` — for the expression core above,
76+
including the Result/`unwrap`/`error` panic-propagation fragment.
77+
78+
## Correspondence map (re PROOF-NEEDS #2)
79+
80+
PROOF-NEEDS #2 ("prove evaluation semantics match the Lean spec") rests on a
81+
**mismatch that must be acknowledged**: the three artefacts model *different*
82+
languages.
83+
84+
- **Lean type system / Result / progress+preservation** ⟷ the **Rust**
85+
`src/typechecker` (Hindley–Milner unification with `Result`, function
86+
types). This is the right correspondence target for the type-safety story.
87+
- **Lean `consent_*`** ⟷ the **spec** `spec/axiomatic-semantics.md` (consent
88+
Hoare triples). In correspondence. ✓
89+
- **`core/eval.ml`** is a *separate, older tree-walking interpreter*: it has
90+
**no Result type**, but adds units-of-measure, `to_bool` coercions, mixed
91+
int/float arithmetic, and structural cross-type `eq`. It is **not** the
92+
language the Lean `Step` relation models.
93+
94+
Consequently, "eval matches the Lean spec" is **not** currently a
95+
well-posed single theorem — the evaluator and the spec diverge. The honest
96+
options are (a) treat `WokeLang.lean` as the **normative spec** and converge
97+
the implementations toward it, or (b) build a *second* Lean model faithful to
98+
`core/eval.ml` (units + coercions + no Result) and prove properties there.
99+
100+
## Recommended next proof steps
101+
102+
1. **Broaden the verified core toward the Rust type system** (each is a small,
103+
pattern-following extension to `Step`/`HasType` + the `progress`/
104+
`preservation` cases, fully checkable):
105+
- integer `sub`/`mul` (total), then `div`/`mod` with a divide-by-zero
106+
`error` step (mirrors `unwrap`-of-`oops` panic propagation already
107+
proven);
108+
- ordering comparisons `lt/gt/le/ge``bool`;
109+
- `or` (mirror of `and`).
110+
2. **Array typing** (`tArray`, an `array` congruence/▸ value rule) to retire
111+
one of the `array`-shaped TODO gaps.
112+
3. **Decide the eval-correspondence question** (a) vs (b) above with the
113+
maintainer before attempting PROOF-NEEDS #2.
114+
4. **Compiler/VM track** (the file's §8 TODO stubs: bytecode, compiler,
115+
VM semantics, compiler-correctness) remains open and is a larger effort.
116+
117+
## Status
118+
119+
- Repair to `sorry`-free under Lean 4.30.0: see CI / `lean` check.
120+
- Coverage: **subset** of the surface language, as tabulated above — *not* a
121+
full-language proof, and honestly so.

0 commit comments

Comments
 (0)