Skip to content

Commit d33624b

Browse files
author
Jonathan D.A. Jewell
committed
build(proofs): reproducible Lean toolchain bootstrap for sandboxed/web sessions
Root-fixes the one dependability gap that blocks future work on this repo: proofs/Tangle.lean is the build oracle and the working rule is "every edit ends with a Lean compile", but a fresh session cannot install Lean — elan resolves toolchains from release.lean-lang.org, which is not on the network allowlist in sandboxed environments (e.g. Claude Code on the web). GitHub release assets ARE reachable. - proofs/bootstrap-lean.sh: idempotent, non-interactive toolchain installer. Reads the version from proofs/lean-toolchain (stays correct across pin bumps); uses the normal elan path on open networks (GitHub Actions) and falls back to the GitHub release asset when the dist server is blocked. `--print-path` emits the PATH export for `eval`. - proofs/README.md: how to build/verify the proofs + why the bootstrap exists. Validated: cold start from an empty ELAN_HOME installs lean 4.14.0 in ~16s and `lean Tangle.lean` reports 0 errors. https://claude.ai/code/session_01PgHpCFzwYB7Qy9L6kmR8CE
1 parent 1aad409 commit d33624b

2 files changed

Lines changed: 167 additions & 0 deletions

File tree

proofs/README.md

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
<!--
2+
SPDX-License-Identifier: MPL-2.0
3+
Owner: Jonathan D.A. Jewell <j.d.a.jewell@open.ac.uk>
4+
-->
5+
# Tangle proofs
6+
7+
Mechanised metatheory for the Tangle core type system, in Lean 4.
8+
9+
- [`Tangle.lean`](Tangle.lean) — the proofs (the repo's **build oracle**).
10+
- [`lean-toolchain`](lean-toolchain) — the pinned Lean version
11+
(`leanprover/lean4:v4.14.0`). Single source of truth for the toolchain.
12+
- [`bootstrap-lean.sh`](bootstrap-lean.sh) — installs that toolchain.
13+
14+
## What is proven
15+
16+
`Tangle.lean` mechanises type safety for the core language, all under Lean's
17+
kernel with **no `sorry`/`axiom`/`admit`** (enforced by CI):
18+
19+
- **Progress, Preservation, Determinism, Type Safety** — for the let-free
20+
fragment *and* the echo-types fragment.
21+
- **Echo types** (structured loss): `Ty.echo`, `echoClose`/`lower`/`residue`,
22+
with the residue-recovery / non-injectivity capstones. See the
23+
`§ECHO-TYPES` section of `Tangle.lean` and
24+
[`../PROOF-NARRATIVE.md`](../PROOF-NARRATIVE.md) §2.5.
25+
- **Decidability** (TG-2): `infer ≡ HasType`, type uniqueness, and a
26+
`Decidable (HasType [] e τ)` instance.
27+
28+
## Building / verifying
29+
30+
```sh
31+
# 1. Install the pinned toolchain (idempotent).
32+
./proofs/bootstrap-lean.sh
33+
34+
# 2. Put lean on PATH for this shell.
35+
eval "$(./proofs/bootstrap-lean.sh --print-path)"
36+
37+
# 3. Verify — 0 errors means the proofs check.
38+
cd proofs && lean Tangle.lean
39+
```
40+
41+
### Why `bootstrap-lean.sh` exists
42+
43+
`elan` (the Lean toolchain manager) resolves toolchains from
44+
`release.lean-lang.org`, which is **not on the network allowlist** in
45+
sandboxed environments such as Claude Code on the web. GitHub release assets
46+
*are* reachable, so when the normal install path is blocked the script fetches
47+
the pinned toolchain directly from `github.com`. On an open network (e.g.
48+
GitHub Actions runners) it uses the normal `elan` path. Either way it reads the
49+
version from `lean-toolchain`, so it stays correct when the pin is bumped.
50+
51+
CI runs the same oracle in [`.github/workflows/lean-proofs.yml`](../.github/workflows/lean-proofs.yml):
52+
`lean Tangle.lean` must report 0 errors and the file must contain no
53+
`sorry`/`axiom`/`admit`/`Admitted` outside comments.

proofs/bootstrap-lean.sh

Lines changed: 114 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,114 @@
1+
#!/usr/bin/env bash
2+
# SPDX-License-Identifier: MPL-2.0
3+
# bootstrap-lean.sh — install the pinned Lean 4 toolchain so the proofs in
4+
# this directory can be built and verified.
5+
#
6+
# WHY THIS EXISTS
7+
# proofs/Tangle.lean is the repo's build oracle (.github/workflows/
8+
# lean-proofs.yml) and the working rule is "every edit ends with a Lean
9+
# compile". GitHub Actions runners have open network and install Lean fine,
10+
# but sandboxed/allowlisted environments (e.g. Claude Code on the web)
11+
# cannot reach elan's default dist server release.lean-lang.org. GitHub
12+
# release assets ARE reachable, so when the normal install path is blocked
13+
# this script fetches the pinned toolchain directly from github.com.
14+
#
15+
# The version is read from proofs/lean-toolchain, so this stays correct
16+
# when the pin is bumped. Idempotent and non-interactive.
17+
#
18+
# USAGE
19+
# ./proofs/bootstrap-lean.sh # install the toolchain
20+
# eval "$(./proofs/bootstrap-lean.sh --print-path)" # and put lean on PATH
21+
# # then:
22+
# cd proofs && lean Tangle.lean # 0 errors == proofs verified
23+
set -euo pipefail
24+
25+
PRINT_PATH=0
26+
[ "${1:-}" = "--print-path" ] && PRINT_PATH=1
27+
28+
# Resolve repo paths relative to this script.
29+
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
30+
PIN_FILE="$SCRIPT_DIR/lean-toolchain"
31+
32+
log() { [ "$PRINT_PATH" -eq 1 ] || echo "bootstrap-lean: $*"; }
33+
34+
if [ ! -f "$PIN_FILE" ]; then
35+
echo "bootstrap-lean: no $PIN_FILE found" >&2
36+
exit 1
37+
fi
38+
39+
PIN="$(tr -d '[:space:]' < "$PIN_FILE")" # e.g. leanprover/lean4:v4.14.0
40+
VER="${PIN##*:}" # v4.14.0
41+
VNUM="${VER#v}" # 4.14.0
42+
ELAN_DIR="${ELAN_HOME:-$HOME/.elan}"
43+
TC_NAME="$(printf '%s' "$PIN" | sed 's|/|--|; s|:|---|')" # leanprover--lean4---v4.14.0
44+
TC_PATH="$ELAN_DIR/toolchains/$TC_NAME"
45+
46+
# --print-path mode: just emit the PATH export (for eval), do no work.
47+
if [ "$PRINT_PATH" -eq 1 ]; then
48+
printf 'export PATH="%s/bin:$PATH"\n' "$ELAN_DIR"
49+
exit 0
50+
fi
51+
52+
export PATH="$ELAN_DIR/bin:$PATH"
53+
54+
# Already bootstrapped — fast idempotent exit.
55+
if [ -x "$TC_PATH/bin/lean" ]; then
56+
log "Lean toolchain $PIN already present at $TC_PATH"
57+
log "Run: eval \"\$($0 --print-path)\" then (cd $SCRIPT_DIR && lean Tangle.lean)"
58+
exit 0
59+
fi
60+
61+
# 1. elan (toolchain manager). raw.githubusercontent.com is reachable.
62+
if [ ! -x "$ELAN_DIR/bin/elan" ]; then
63+
log "installing elan…"
64+
curl -sSf https://raw.githubusercontent.com/leanprover/elan/master/elan-init.sh \
65+
| sh -s -- -y --default-toolchain none >/dev/null
66+
fi
67+
68+
# 2. Try the normal toolchain install; fall back to the GitHub release asset
69+
# if the elan dist server is unreachable (the allowlist case).
70+
if "$ELAN_DIR/bin/elan" toolchain install "$PIN" >/dev/null 2>&1 \
71+
&& [ -x "$TC_PATH/bin/lean" ]; then
72+
log "installed $PIN via elan."
73+
else
74+
log "elan dist server unreachable — using GitHub release asset."
75+
arch="$(uname -m)"
76+
case "$arch" in
77+
x86_64) asset="lean-${VNUM}-linux.tar.zst" ;;
78+
aarch64) asset="lean-${VNUM}-linux_aarch64.tar.zst" ;;
79+
*) echo "bootstrap-lean: unsupported arch '$arch'" >&2; exit 1 ;;
80+
esac
81+
url="https://github.com/leanprover/lean4/releases/download/${VER}/${asset}"
82+
tmp="$(mktemp -d)"
83+
trap 'rm -rf "$tmp"' EXIT
84+
log "downloading $url"
85+
curl -sSfL -o "$tmp/lean.tar.zst" "$url"
86+
87+
# zstd is needed to unpack .tar.zst.
88+
if ! command -v unzstd >/dev/null 2>&1 && ! command -v zstd >/dev/null 2>&1; then
89+
if command -v apt-get >/dev/null 2>&1; then
90+
sudo apt-get install -y zstd >/dev/null 2>&1 \
91+
|| apt-get install -y zstd >/dev/null 2>&1 || true
92+
fi
93+
fi
94+
95+
mkdir -p "$ELAN_DIR/toolchains"
96+
tar --use-compress-program=unzstd -xf "$tmp/lean.tar.zst" -C "$tmp"
97+
extracted="$(find "$tmp" -maxdepth 1 -type d -name 'lean-*' | head -1)"
98+
if [ -z "$extracted" ]; then
99+
echo "bootstrap-lean: extraction produced no lean-* directory" >&2
100+
exit 1
101+
fi
102+
rm -rf "$TC_PATH"
103+
mv "$extracted" "$TC_PATH"
104+
log "installed $PIN from GitHub release."
105+
fi
106+
107+
# 3. Sanity check.
108+
if [ -x "$TC_PATH/bin/lean" ]; then
109+
log "ready — $("$TC_PATH/bin/lean" --version 2>/dev/null || echo '(version unavailable)')"
110+
log "next: eval \"\$($0 --print-path)\" then (cd $SCRIPT_DIR && lean Tangle.lean)"
111+
else
112+
echo "bootstrap-lean: toolchain bootstrap FAILED" >&2
113+
exit 1
114+
fi

0 commit comments

Comments
 (0)