Some checks are pending
Lean Action CI / build (push) Waiting to run
Implements the cells-spec vision: a computation space that preserves auditability, correctness, interactivity. Phase 1 (Lean kernel + naga-IR Rust backend) is closed; foundation hypothesis stack (Selection H1+H2, Subobject H3, Trace H5, Obs.Ctx C2, Cubical.Trace) landed. Highlights: - Cubical-HoTT syntax + value/eval/readback in Lean - naga-IR pipeline (no GLSL string crosses FFI; 17/17 probes pass) - Honesty audit: every non-transport (sealed cells, vertex shader, Y-flip, presentation conventions) is documented as such - Polymorphic Trace α as free monoid; Cubical.Trace gives CTerm → Trace CTerm by structural fold (homomorphism = definition) - Selection as Huet zipper; Subobject as Boolean algebra over WCell - All theorems proven; the proof IS the implementation See STATUS.md for the resume guide. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1163 lines
69 KiB
Markdown
1163 lines
69 KiB
Markdown
# Topolei — Formal Status
|
||
|
||
*Last updated: 2026-04-24 (Phase 1 closed; Stages 1–4 delivered;
|
||
Rust backend Phases A–D all shipped. Rust cubical evaluator is
|
||
live in both native and wasm32 builds, wired via @[implemented_by],
|
||
verified by 55/55 smoke+property tests and baseline benchmarks.)*
|
||
|
||
---
|
||
|
||
## Resume guide (start here on a new session)
|
||
|
||
This document combines architecture, axiom inventory, historical logs, and
|
||
the next-action menu. For resuming work, read in this order:
|
||
|
||
1. **Architecture at a glance** (immediately below) — the Lean-as-host /
|
||
Rust-as-FFI-backend framing. Skip if already familiar.
|
||
2. **What is done** (jump to `## What is done` heading) — current module
|
||
inventory.
|
||
3. **Axiom discharge obligations** (`## Axiom discharge obligations`) —
|
||
the live list of what Rust must compute.
|
||
4. **Priority order** (`## Priority order`) — the menu of next-action
|
||
entry points across Streams A/B/C, with ✅ marks on completed items.
|
||
5. **Concrete "next session" suggestions** (last section) — curated
|
||
single/multi-session candidates.
|
||
|
||
Skip the per-week historical logs (the long block of "Phase 1 Week N"
|
||
sub-sections) unless tracing a specific past decision.
|
||
|
||
**Process discipline (do not violate):**
|
||
- Lean-first, Rust-second: every Rust-implemented behavior is *first*
|
||
stated in Lean as an axiom, *then* discharged. Adding eval/readback
|
||
axioms in Lean is the right move; writing Rust ahead of the spec is not.
|
||
- Face-disjoint axiom partitions: when extending a sub-case (e.g. Glue
|
||
transport), add new axioms with a precondition disjoint from existing
|
||
ones rather than rewriting the existing axiom. This keeps `transp_ua`
|
||
and similar consumers stable.
|
||
- NbE-first reductions: prefer eval-level + readback-level axioms over
|
||
step-level. After Stage 2.3 (subject reduction), **T3 and C4 are
|
||
theorems**, not step-level axioms; only T4 (`transp_plam_is_plam`)
|
||
remains as a syntactic fallback — all other step-level axioms are gone.
|
||
- **Axiom provenance discipline (Stage 3):** every axiom carries one of
|
||
three provenance tags — *Rust-discharge* (evaluator implementation),
|
||
*Lean-discharge* (future Lean proof from inductive definition), or
|
||
*IEEE/external* (Float / GPU spec). See "Axiom provenance spectrum"
|
||
section below.
|
||
|
||
---
|
||
|
||
## Architecture at a glance
|
||
|
||
**Topolei is a Lean 4 extension that adds cubical-transport HoTT to Lean 4
|
||
via a Rust FFI module.** The process discipline is:
|
||
|
||
1. **Formalize in Lean first.** Every layer that will eventually be
|
||
implemented in Rust — the cubical evaluator's reduction rules, the GPU
|
||
runtime's IO behavior, numerical kernels' input-output specs — is
|
||
*first* stated in Lean as axioms and data structures. No Rust is
|
||
written until the Lean-side axiom set is stable.
|
||
2. **Discharge via FFI later.** Rust functions, exposed through a C ABI,
|
||
are linked back into Lean via `@[extern]` + `@[implemented_by]`. Each
|
||
axiom becomes a kernel-trusted computation.
|
||
3. **Phase 1 (Cubical Core) is closed in Lean.** The six-week cubical
|
||
specification (Interval, Face, Syntax, Subst, DimLine, Typing, Value,
|
||
Eval, Transport, Comp, Equiv, Glue, Soundness, plus the EML and
|
||
GPU/Spec bridges) is complete with zero Rust dependency. Its axiom
|
||
set is the contract the Rust FFI will eventually satisfy.
|
||
4. **Many Lean-side phases don't need Rust.** Phase 2 (Cells), Phase 3a
|
||
(Reactive), Phase 3b (Color), Phase 4 Shader IR/emit, Phase 5b
|
||
Boundary/Security models, and Phase 6 Meta can all proceed as more
|
||
Lean — extending the axiom base. The Rust FFI unblocks *execution*
|
||
(GPU, window, OS), not *formalization*.
|
||
|
||
**The one Rust component has two distinct surfaces within it:**
|
||
|
||
- **(a) Cubical evaluator backend** — Rust implements `step`, `eval`,
|
||
`vApp`, `vPApp`, `vTransp`, `vHCompValue`, `vCompAtTerm`, `vCompNAtTerm`
|
||
+ the face-disjoint reduction rules for transp/comp/glue. Gives Lean's
|
||
kernel the ability to *reduce* cubical terms to values. **This is the
|
||
defining purpose of topolei's Rust FFI** — it exists to extend Lean 4
|
||
with computational cubical-transport HoTT.
|
||
- **(b) GPU / effectful runtime** — wgpu-backed GPU context, shader
|
||
upload/dispatch, window/input, OS primitives. Hosted in the same
|
||
Rust crate for build convenience; distinct FFI surface. Specs live
|
||
in `GPU/Spec.lean` + Phase 5 Runtime modules.
|
||
|
||
**Zigzag engine — Lean, not Rust.** The combinatorial n-category
|
||
engine (normalisation, degeneracy theory, type-checking against
|
||
signatures) is being ported into Lean. The Rust reference at
|
||
`zigzag-engine/` is a port-from template, not a dependency. See
|
||
`ZIGZAG_PORT.md` for the full 10-step plan. This preserves the
|
||
Lean-as-host discipline: reasoning inside Lean about n-categorical
|
||
normalisation requires structural access, which FFI hides.
|
||
|
||
**Numerical scheme discipline:** see `NUMERICAL.md` for the
|
||
context-from-content separation principle applied to numerical
|
||
implementations. Every scheme is a pair (mathematical content ×
|
||
execution context); they must not leak into each other, and §10
|
||
defines the minimum contract for first-class registry status.
|
||
|
||
---
|
||
|
||
## Audit summary
|
||
|
||
Post-review pass addressed a kernel-level soundness issue and cleaned up
|
||
abstractions across Phase 1. Changes:
|
||
|
||
- **Soundness:** `CTerm.step` is now `opaque` (was a concrete `def` with an
|
||
identity wildcard arm). Every axiom of the form `step (.transp …) = …` or
|
||
`step (.comp …) = …` was silently inconsistent with `CTerm.noConfusion`
|
||
under the old definition; opacity restores consistency. The path β-rule
|
||
is stated as an explicit axiom `step_papp_plam`.
|
||
- **CCHM alignment:** dropped the non-CCHM axiom `transp_as_comp` (T3) and
|
||
the misleading `transp_id_from_comp` derived theorem. `comp_top_const_id`
|
||
now requires `CTerm.dimAbsent i t = true` and is reproved via `comp_full` +
|
||
`substDimBool_of_absent`, with no appeal to T3.
|
||
- **Dead code:** collapsed identical `if j = i` branches in
|
||
`CTerm.substDim` (Syntax) and `CTerm.dimAbsent` (DimLine). Removed
|
||
tautology theorems `substDim_false_is_at0`/`substDim_true_is_at1`.
|
||
- **Abstraction:** removed useless hypotheses on `comp_full`
|
||
(`(φ : FaceFormula) (hφ : φ = .top)`) and `comp_empty`
|
||
(`Entails .bot .bot`). `comp_full_typed` now *actually* uses `comp_full`
|
||
(states typing for `substDimBool L.binder true u`, not for `step`).
|
||
`path_at_face_zero`/`one` removed (they took unused face hypotheses;
|
||
the unconditional `CTerm.step_papp_zero`/`one` in Syntax already
|
||
capture the content). `typing_plam_boundaries` renamed to
|
||
`plam_boundaries` and stripped of its unused typing hypothesis.
|
||
`System.typed_bot_vacuous` renamed to `System.typed_bot`.
|
||
|
||
### GPU bridge pass
|
||
|
||
- **Soundness (GPU side):** the old `compile_correct` axiom universally
|
||
quantified over both `expr` and `h`, so instantiating with two different
|
||
exprs at a shared `h` forced `expr₁.toColor = expr₂.toColor`. Replaced with
|
||
an abstract compile function `compileEML : EMLExpr → ShaderHandle` and a
|
||
per-handle correctness axiom `compileEML_correct`.
|
||
- **Rendering bridge:** added `EMLPath.evalAt_body_eq_at1` (scalar),
|
||
`EMLPath.toColor_body_eq_at1_toColor` (color), and `render_eq_at1`
|
||
(end-to-end). These close the cubical → Float → color → pixel chain.
|
||
- **Plot paths:** added `PlotConfig.dimName` and `PlotConfig.toEMLPath` in
|
||
EML/Path.lean. `plotExp` and `plotLn` are proved *constant paths* (their
|
||
bodies don't mention `"t"`); a genuinely parametric `plotExpT` demo was
|
||
added with proved endpoint values.
|
||
- **Last sorry discharged:** `evalAt_expOf` now closes against three IEEE 754
|
||
Float axioms (`Float.log_one`, `Float.max_one_ge_eps`, `Float.sub_zero`).
|
||
|
||
### Phase 1 Week 2: evaluator (cells-spec §5.4)
|
||
|
||
- **`Cubical/Value.lean`:** mutual `CEnv` / `CVal` / `CNeu` inductives
|
||
(named-variable adaptation of the spec's de-Bruijn `Env`/`Val`/`Neutral`).
|
||
`CVal` covers `vlam` closures, `vplam` dim-closures, and embedded `CNeu`;
|
||
`CNeu` covers free variables, stuck (p)apps, and stuck transport/comp.
|
||
- **`Cubical/Eval.lean`:** mutual `partial def` `eval` / `vApp` / `vPApp`
|
||
covering the λ-calculus fragment plus dimension application (β-reduces
|
||
via `CTerm.substDim`). Transport and composition produce `vneu` stuck
|
||
values (real reduction rules are Phase 1 Weeks 3–4). Eleven axiom
|
||
equations mirror the match arms so theorems can reduce through `eval`
|
||
without relying on `partial def`'s opaque kernel form — same pattern as
|
||
`opaque CTerm.step` + `step_papp_plam`.
|
||
- **`Cubical/EvalTest.lean`:** eight roundtrip tests — free variable,
|
||
identity β, constant-function β, K combinator (two β-reductions), stuck
|
||
application, path-abstraction closure, path β via `substDim`, transport
|
||
and composition neutralisation. All proved from the axiom equations.
|
||
|
||
### Phase 1 Week 3: transport at eval level (cells-spec §5.5)
|
||
|
||
**Full CCHM Π transport is implemented** — both constant-domain and
|
||
varying-domain cases. Delivers the "transport along refl = id" test
|
||
target (§13 Week 3) and the per-type-former Π rule.
|
||
|
||
#### Prerequisite: CType substitution by general DimExpr
|
||
|
||
- **`Cubical/Subst.lean`:** added `CType.substDimExpr : DimVar → DimExpr → CType → CType`
|
||
— generalises the existing Bool-only `CType.substDim` to arbitrary
|
||
`DimExpr`. Needed to build the reversed line `A[i := inv i]` for
|
||
inverse transport. Reduction lemmas `substDimExpr_univ/pi/path` and the
|
||
bridge `substDim_eq_substDimExpr` relating the two.
|
||
- **`Cubical/DimLine.lean`:** generalised `CTerm.substDim_of_absent` to
|
||
arbitrary `DimExpr` (the proof is the same as the old Bool case); added
|
||
`CType.substDimExpr_of_absent`. The old `substDimBool_of_absent` is now
|
||
a corollary.
|
||
|
||
#### Value-level machinery
|
||
|
||
- **`Cubical/Value.lean`:** `CVal.vTranspFun` now carries *both* domain
|
||
and codomain: `vTranspFun : DimVar → CType → CType → FaceFormula →
|
||
CVal → CVal`. A single uniform constructor handles the full CCHM Π
|
||
rule; the constant-domain specialisation falls out automatically from
|
||
`vTranspInv_const`.
|
||
- **`Cubical/Transport.lean`:**
|
||
- `vTransp i A φ v` dispatches in four priority-ordered arms:
|
||
1. `φ = .top` → `v` (T1).
|
||
2. `CType.dimAbsent i A = true` → `v` (T2).
|
||
3. `A = pi domA codA` → `vTranspFun i domA codA φ v` (**unified
|
||
full CCHM Π rule** — no longer gated by `dimAbsent i domA`).
|
||
4. Otherwise → stuck `vneu (.ntransp i A φ v)`.
|
||
- `vTranspInv i A φ v := vTransp i (A.substDimExpr i (.inv (.var i))) φ v`
|
||
— inverse transport as forward transport along the reversed line.
|
||
- Theorem `vTranspInv_const`: when `CType.dimAbsent i A = true`, the
|
||
reversed line equals the original (by `substDimExpr_of_absent`), so
|
||
`vTranspInv` reduces to `v` via T2.
|
||
- Theorem `vTranspInv_top`: `vTranspInv i A .top v = v`.
|
||
- Four axioms: `vTransp_top`, `vTransp_const`, `vTransp_pi` (new;
|
||
replaces the earlier `vTransp_pi_dom_const`), `vTransp_stuck`
|
||
(simplified precondition: "A is not a pi" replaces the earlier
|
||
"not a pi-with-constant-domain").
|
||
|
||
#### CCHM Π β-rule at the value level
|
||
|
||
- **`Cubical/Eval.lean`:** `vApp` on a transported function now performs
|
||
the full three-step CCHM reduction:
|
||
```
|
||
vApp (.vTranspFun i domA codA φ f) arg =
|
||
vTransp i codA φ (vApp f (vTranspInv i domA φ arg))
|
||
```
|
||
i.e. inverse-transport through the domain, apply the function,
|
||
forward-transport through the codomain. The axiom `vApp_vTranspFun`
|
||
is updated to match. Derived theorem `eval_transp_pi` replaces the
|
||
earlier `eval_transp_pi_dom_const`.
|
||
|
||
#### Tests
|
||
|
||
- `eval_transp_top_id`, `eval_transp_const_univ`, `eval_transp_const_pi`
|
||
— Week 3 "transport along refl = id".
|
||
- `eval_transp_pi_const_dom_example` — const-domain Π transport produces
|
||
`vTranspFun i .univ codA φ f`.
|
||
- `vApp_vTranspFun_const_dom_reduces` — applying it reduces through
|
||
`vTranspInv_const` (identity) + outer `vTransp_stuck`.
|
||
- `eval_transp_pi_varying_dom` — **varying-domain** Π transport also
|
||
produces `vTranspFun` (same constructor, no special case).
|
||
- `vApp_vTranspFun_varying_dom_reduces` — applying it cascades three
|
||
stuck neutrals: the inverse transport through the varying domain, the
|
||
stuck application, and the stuck forward transport through the
|
||
varying codomain.
|
||
|
||
### Correctness audit
|
||
|
||
The axiom set is provably disjoint by preconditions (top / const / pi /
|
||
stuck-non-pi), so no two axioms can fire with contradictory conclusions
|
||
on the same input. The unified `vTranspFun` constructor is
|
||
semantically well-behaved at every edge case:
|
||
|
||
- **Fully constant line** (caught by arm 2): `vTranspFun` isn't produced;
|
||
if manually constructed, `vApp` reduces it to `vApp f y` via the
|
||
two-sided `vTransp_const`/`vTranspInv_const` collapse.
|
||
- **Constant domain, varying codomain**: `vTranspInv` reduces to
|
||
identity, recovering the simpler formula `vTransp i codA φ (vApp f arg)`.
|
||
- **Varying domain**: the full CCHM three-step reduction runs; when
|
||
pieces stall, they stall consistently into nested `ntransp`/`napp`
|
||
neutrals.
|
||
- **Under `φ = .top`** (if manually constructed): reduces to `vApp f y`
|
||
via `vTransp_top` + `vTranspInv_top` — correct, since identity
|
||
transport of a function is the function.
|
||
|
||
### Phase 1 Week 4: composition at eval level
|
||
|
||
Heterogeneous composition at eval level plus homogeneous composition on
|
||
Π types — CCHM §5.6 delivered with working reductions for the cases that
|
||
don't require Glue.
|
||
|
||
#### Value-level machinery
|
||
|
||
- **`Cubical/Value.lean`:** two new `CVal` constructors + one `CNeu`:
|
||
- `vHCompFun : CType → FaceFormula → CVal → CVal → CVal` — result of
|
||
`hcomp (pi A B) φ tube base` at the value level. No domain stored
|
||
(homogeneous comp doesn't transport through the domain).
|
||
- `vTubeApp : CVal → CVal → CVal` — represents `λj. (tube @ j) arg`
|
||
as a dim-abstraction value. Needed to thread the outer hcomp's
|
||
tube into the inner hcomp on the codomain.
|
||
- `CNeu.nhcomp : CType → FaceFormula → CVal → CVal → CNeu` — stuck
|
||
hcomp (separate from `ncomp` because the type is fixed, not a line).
|
||
|
||
#### Evaluator extensions
|
||
|
||
- **`Cubical/Eval.lean`:** mutual block extended with two partial defs:
|
||
- `vHCompValue A φ tube base` — homogeneous composition on a fixed
|
||
type. Three disjoint arms: `.top → vPApp tube .one`, `.pi → vHCompFun`,
|
||
stuck otherwise.
|
||
- `vCompAtTerm env i A φ u t` — heterogeneous composition at the *term
|
||
level*. Takes `u` and `t` as `CTerm`s so that the `comp_full` case
|
||
can substitute `i := 1` before evaluating (this is genuinely different
|
||
from `vPApp`-ing the evaluated `u`). Four disjoint arms:
|
||
`.top → eval env (u[i := 1])` (C1), `.bot → eval env (.transp i A .bot t)` (C2),
|
||
`dimAbsent i A → vHCompValue A φ (vplam env i u) (eval env t)`
|
||
(hetero-comp ≡ hcomp on constant lines), stuck otherwise.
|
||
- `eval`'s `.comp` arm now routes through `vCompAtTerm`.
|
||
- `vApp` gains a `vHCompFun` case unfolding per the CCHM Π hcomp rule:
|
||
`vApp (.vHCompFun codA φ tube base) arg = vHCompValue codA φ (.vTubeApp tube arg) (vApp base arg)`.
|
||
- `vPApp` gains a `vTubeApp` case: `vPApp (.vTubeApp tube arg) r = vApp (vPApp tube r) arg`.
|
||
- Exhaustiveness cases for `vApp` on `vTubeApp` (type error) and
|
||
`vPApp` on `vHCompFun` (type error).
|
||
|
||
#### Axioms (all disjoint by precondition)
|
||
|
||
Old coarse `eval_comp` axiom replaced by four disjoint case-axioms:
|
||
- `eval_comp_top` — C1: `eval env (u.substDim i .one)`.
|
||
- `eval_comp_bot` — C2: `eval env (.transp i A .bot t)`.
|
||
- `eval_comp_const` — `dimAbsent i A → vHCompValue A φ (vplam env i u) (eval env t)`.
|
||
- `eval_comp_stuck` — fallback.
|
||
|
||
Three `vHCompValue_*`: `vHCompValue_top`, `vHCompValue_pi`, `vHCompValue_stuck`.
|
||
One each for `vApp` on `vHCompFun` (`vApp_vHCompFun`) and `vPApp` on
|
||
`vTubeApp` (`vPApp_vTubeApp`).
|
||
|
||
#### Tests
|
||
|
||
- `eval_comp_top_example` — C1 with a body with no dim dep → stripped to neutral.
|
||
- `eval_comp_top_dim_subst` — C1 with body `papp (var "p") i`; the `i := 1`
|
||
substitution hits `DimExpr.subst (var i)` which collapses to `DimExpr.one`,
|
||
yielding `npapp (nvar "p") .one`. **Exercises real dim substitution.**
|
||
- `eval_comp_bot_univ` — C2 chained with T2: `comp i .univ .bot u t` → `eval t`.
|
||
- `eval_comp_const_line` — constant-line comp reduces to hcomp; on `.univ`
|
||
the hcomp stalls into `nhcomp`.
|
||
- `eval_comp_neu` — stuck comp form (free vars, non-const non-endpoint face).
|
||
- `vHCompValue_top_reduces` — `hcomp .top` on a vplam tube returns the
|
||
tube-body at dim 1.
|
||
- `vApp_vHCompFun_reduces` — **CCHM Π hcomp β-rule** runs with neutral
|
||
tube/base/argument; cascades cleanly into `nhcomp` + `napp` neutrals.
|
||
- `vPApp_vTubeApp_reduces` — `(λj. tube@j arg) @ r` reduces to
|
||
`(tube@r) arg` using a vplam tube.
|
||
|
||
### C1 / C2 now discharged at eval level
|
||
|
||
`eval_comp_top` and `eval_comp_bot` are the eval-level analogues of the
|
||
step-level axioms `comp_full` (C1) and `comp_empty` (C2). Once a
|
||
`step ↔ eval` bridge is built, the step-level axioms can be derived from
|
||
these — one less foundational axiom each.
|
||
|
||
### Phase 1 Week 4+: Path transport (cells-spec §5.5 Path case)
|
||
|
||
Path transport of a path element `p : Path A(0) a(0) b(0)` along a line
|
||
`i. Path A(i) a(i) b(i)` produces an element of `Path A(1) a(1) b(1)`.
|
||
CCHM's rule uses a 3-clause heterogeneous comp with the system
|
||
`[φ ↦ p@j, j=0 ↦ a(i), j=1 ↦ b(i)]`. Full multi-clause comp is an
|
||
involved refactor; this pass delivers the **endpoint-accurate
|
||
reductions** (which cover the common case of evaluating paths at their
|
||
boundaries) and a **structured stuck form** for generic-dim applications
|
||
that preserves the transport data for possible future unsticking.
|
||
|
||
#### Value-level machinery
|
||
|
||
- **`Cubical/Value.lean`:**
|
||
- `CVal.vPathTransp : CEnv → DimVar → CType → CTerm → CTerm → FaceFormula → CVal → CVal`
|
||
— value-level representation of `transp^i (Path A(i) a(i) b(i)) φ p`.
|
||
Stores env (for later substDim on `a`, `b`), binder, base type,
|
||
endpoint CTerms, face, and the already-evaluated path value.
|
||
- `CNeu.npathTranspApp : ... → DimExpr → CNeu` — generic-dim stall,
|
||
structurally preserves the full transport data.
|
||
|
||
#### Evaluator refactor
|
||
|
||
- **`Cubical/Eval.lean`:** eval's `.transp` arm now dispatches in four
|
||
priority-ordered arms:
|
||
1. `φ = .top` → `eval env t` (T1, unconditional).
|
||
2. `CType.dimAbsent i A = true` → `eval env t` (T2, covers constant
|
||
lines of *any* head — univ, pi, path, sigma-when-we-have-it).
|
||
3. `A = .path A₀ a b` (line varies) → `vPathTransp` closure.
|
||
4. Otherwise → value-level `vTransp` (which handles `.pi` via
|
||
`vTranspFun`).
|
||
`vPApp` gains three new arms for `vPathTransp`:
|
||
· At `.zero` → `eval env (a.substDim i .one)` (= a(1) from CCHM
|
||
(j=0) clause).
|
||
· At `.one` → `eval env (b.substDim i .one)` (= b(1) from (j=1)).
|
||
· At generic DimExpr → `vneu (.npathTranspApp …)`.
|
||
`vApp` gains a `vPathTransp` case (type error — path values aren't
|
||
functions).
|
||
|
||
#### Axioms (all disjoint by precondition)
|
||
|
||
Old coarse `eval_transp` axiom replaced by four disjoint case-axioms:
|
||
- `eval_transp_top` (φ = .top).
|
||
- `eval_transp_const` (φ ≠ .top ∧ dimAbsent i A).
|
||
- `eval_transp_path` (φ ≠ .top ∧ path-line varies).
|
||
- `eval_transp_nonpath` (φ ≠ .top ∧ non-constant non-path).
|
||
|
||
Three axioms for `vPApp` on `vPathTransp`: `vPApp_vPathTransp_zero`,
|
||
`vPApp_vPathTransp_one`, `vPApp_vPathTransp_stuck` (r ≠ .zero ∧ r ≠ .one).
|
||
|
||
#### Tests
|
||
|
||
- `eval_transp_path_example` — transport along a varying path line
|
||
`path univ (var "a_line") (papp (var "b_pt") i)` produces the
|
||
expected `vPathTransp` closure.
|
||
- `vPApp_vPathTransp_zero_reduces` — endpoint at `.zero` yields `a(1)`.
|
||
Exercises `substDim i .one` on a constant endpoint term.
|
||
- `vPApp_vPathTransp_one_reduces` — endpoint at `.one` yields `b(1)`.
|
||
The `.papp (.var "b_pt") (DimExpr.var i)` endpoint has its `i` replaced
|
||
with `.one`, producing `npapp (nvar "b_pt") .one` — **real CCHM
|
||
endpoint correction firing through multiple layers**.
|
||
- `vPApp_vPathTransp_stuck_reduces` — at a fresh dim var, stalls at
|
||
`npathTranspApp` preserving all transport data.
|
||
- `eval_transp_constant_path` — when the path line is fully constant,
|
||
T2 fires and the transport is identity (no `vPathTransp` produced).
|
||
|
||
### Correctness audit for path transport
|
||
|
||
- **Axiom disjointness** — eval's four `.transp` axioms have preconditions
|
||
that partition the input space: `.top` / (non-top ∧ const) / (non-top
|
||
∧ non-const ∧ path) / (non-top ∧ non-const ∧ non-path). Similarly the
|
||
three `vPApp vPathTransp` axioms partition by `.zero`/`.one`/else.
|
||
- **Endpoint correctness** — the CCHM `(j=0)` clause specifies the value
|
||
at `j=0` to be `a(i)`; at the lid `i=1`, this is `a(1)`. Not
|
||
`transp^i(a(0))`, which is what a naïve approach would compute. Our
|
||
axiom directly gives the CCHM-correct value `eval env (a.substDim i .one)`.
|
||
- **Constant-line short-circuit** — when `dimAbsent i A = true` for
|
||
`A = .path A₀ a b`, CCHM transport is identity (T2). Our eval checks
|
||
this before the path routing, so no `vPathTransp` is constructed and
|
||
the result is plainly `eval env t`. Avoids producing a `vPathTransp`
|
||
that would need additional reduction machinery to recognize as identity.
|
||
- **φ = .top short-circuit** — similarly, the top face is caught before
|
||
path routing, so transport under `.top` is identity (T1) for path
|
||
types as for any other type. No `vPathTransp` is produced.
|
||
- **Stall preservation** — at generic dims, the `npathTranspApp` neutral
|
||
stores env, binder, types, endpoints, face, and path value. A later
|
||
substitution of a fresh dim with an endpoint can be propagated through
|
||
this neutral via the endpoint axioms — no data is lost.
|
||
|
||
### Phase 1 Week 4++: Multi-clause comp + full path transport
|
||
|
||
The **full CCHM machinery** for path transport is now in place — generic-dim
|
||
applications no longer stall blindly but produce a multi-clause `compN` that
|
||
genuinely unsticks when any clause face resolves to `.top` (via the new
|
||
`FaceFormula.dimExprEq0/1` helpers).
|
||
|
||
#### New machinery
|
||
|
||
**`Cubical/Face.lean`**:
|
||
- `FaceFormula.dimAbsent` (moved up from DimLine so it's visible at Face level).
|
||
- `FaceFormula.dimExprEq0 / dimExprEq1 : DimExpr → FaceFormula` — encoding
|
||
"r = 0" and "r = 1" as face formulas for composite DimExprs. Mutual
|
||
recursion (inv swaps them; meet/join de Morgan-dualise).
|
||
`_eval` theorems prove semantic correctness.
|
||
- `FaceFormula.substDim : DimVar → DimExpr → FaceFormula → FaceFormula` —
|
||
general substitution. Uses `dimExprEq0/1` for `eq0 j[j:=r]`/`eq1 j[j:=r]`.
|
||
- Supporting theorems: `substDim_zero/substDim_one` (agree with `restrict`),
|
||
`substDim_of_absent`, `substDim_comm`, `dimExprEq0_dimAbsent`/`dimExprEq1_dimAbsent`,
|
||
`dimExprEq0_substDim_of_absent`/`dimExprEq1_substDim_of_absent`,
|
||
`dimAbsent_after_substDim`.
|
||
|
||
**`Cubical/Interval.lean`**: `DimExpr.dimAbsent` relocated here.
|
||
|
||
**`Cubical/Syntax.lean`**:
|
||
- New CTerm constructor `compN : DimVar → CType → List (FaceFormula × CTerm) → CTerm → CTerm`.
|
||
- `CTerm.substDim` extended for `compN` via a mutual helper
|
||
`substDim.clauses` (so Lean can see structural termination through the
|
||
clause list).
|
||
- `transp` and `comp` now substitute into `FaceFormula` too (were previously
|
||
no-ops on `φ` — an approximation that could produce subtle bugs with
|
||
nested dim substitutions).
|
||
|
||
**`Cubical/DimLine.lean`**:
|
||
- `CTerm.dimAbsent` extended for `compN`, mutual helper `dimAbsent.clauses`.
|
||
- `CTerm.substDim_absent_aux`, `CTerm.dimAbsent_after_substDim_aux`,
|
||
`CTerm.substDim_comm_aux` all extended to handle `compN` via mutual
|
||
helpers for the clause-list case.
|
||
- Transp/comp proofs updated for the new face-formula substitution.
|
||
|
||
**`Cubical/Value.lean`**:
|
||
- `CNeu.ncompN` — stuck multi-clause comp neutral.
|
||
- `CVal.vPathTransp` changed to store the path as a `CTerm` (not CVal) —
|
||
needed so `.papp p r` terms can be constructed for the multi-clause
|
||
reduction.
|
||
- `CNeu.npathTranspApp` removed (no longer produced — path transport now
|
||
always reduces to `vCompNAtTerm`, which handles the stuck case itself).
|
||
|
||
**`Cubical/Eval.lean`**:
|
||
- Mutual block now has **six** partial defs: `eval`, `vApp`, `vPApp`,
|
||
`vHCompValue`, `vCompAtTerm`, `vCompNAtTerm`.
|
||
- `eval`'s `.compN` arm delegates to `vCompNAtTerm`.
|
||
- `vPApp` on `vPathTransp` at a generic DimExpr now routes through
|
||
`vCompNAtTerm` with the CCHM 3-clause system
|
||
`[(φ, p@r), (dimExprEq0 r, a), (dimExprEq1 r, b)]` and base `p@r`.
|
||
**This is the genuine CCHM path transport reduction, not a stall.**
|
||
- `vCompNAtTerm` priority-order arms:
|
||
· forcing `.top` clause anywhere in list → fires (substDim i .one in body).
|
||
· empty list → reduces to plain transport (C2).
|
||
· single live clause → delegates to `vCompAtTerm`.
|
||
· else → stuck `ncompN`.
|
||
- New axioms: `eval_compN`, `vCompNAtTerm_def` (compound axiom exposing
|
||
the full case analysis — users pattern-match on clause list).
|
||
- `vPApp_vPathTransp_general` replaces the old `vPApp_vPathTransp_stuck`
|
||
(now uses `vCompNAtTerm` reduction).
|
||
|
||
#### New tests
|
||
|
||
- `eval_compN_empty` — empty system compN reduces to transport.
|
||
- `eval_compN_top_fires` — top-clause at head fires (C1-like).
|
||
- `eval_compN_top_later` — top-clause deeper in list still fires
|
||
(`find?` scans past non-top clauses).
|
||
- `vPApp_vPathTransp_inv_zero` — **key correctness test**: applying path
|
||
transport at the composite DimExpr `.inv .zero` (semantically = 1)
|
||
exercises:
|
||
1. `dimExprEq0 (inv 0)` computes via the de Morgan case to
|
||
`dimExprEq1 .zero = .bot`.
|
||
2. `dimExprEq1 (inv 0)` computes to `dimExprEq0 .zero = .top`.
|
||
3. compN system `[(φ, _), (.bot, a), (.top, b)]` scanned by `find?`.
|
||
4. `.top`-faced clause fires → returns `b(1)` = `b.substDim i .one`.
|
||
5. `pathLine_b.substDim i_dim .one` = `papp (var "b_pt") .one`.
|
||
6. Evaluates to `vneu (npapp (nvar "b_pt") .one)`.
|
||
This is real CCHM reduction chaining through every new piece.
|
||
|
||
### Correctness audit for this pass
|
||
|
||
- **`FaceFormula.substDim` preserves semantic equality with `restrict`
|
||
at endpoints** — `substDim_zero/one = restrict false/true`. Full
|
||
semantic correctness proven via `dimExprEq0_eval` / `dimExprEq1_eval`
|
||
theorems (`dimExprEq0 r` evaluates to true iff `r` evaluates to false).
|
||
- **compN clause-list substitution commutes with disjoint dim vars** —
|
||
via `CTerm.substDim.clauses_comm_aux` (mutual with the CTerm case).
|
||
- **`.compN` dim-absence preserved by substitution** — via
|
||
`CTerm.dimAbsent.clauses_after_substDim` (mutual).
|
||
- **Face formulas now substituted in `.transp` and `.comp`** — closes a
|
||
prior gap where `transp i A φ t`'s dim-substitution left `φ` unchanged
|
||
even when `φ` mentioned the substituted var.
|
||
- **Path transport at endpoints still via specific axioms**
|
||
(`vPApp_vPathTransp_zero/one`) — semantic-equivalent shortcut to the
|
||
general compN reduction, skipping list-scan overhead.
|
||
|
||
### Phase 1 Week 5: Equivalences and Glue (cells-spec §5.7–§5.8)
|
||
|
||
**Equiv** (`Cubical/Equiv.lean`):
|
||
|
||
- `EquivData` — half-adjoint equivalence structure (HoTT §4.2) carrying
|
||
`f`, `fInv`, `sec`, `ret`, `coh` as raw `CTerm`s rather than a Σ-type
|
||
(sigma types aren't in `CType` yet; `fiberTy` / contractibility-of-fibers
|
||
deferred).
|
||
- `idEquiv : CType → EquivData` — trivial equivalence on any type;
|
||
uses reserved hygienic names `"$x"`, `⟨"$e"⟩`, `⟨"$eo"⟩`, `⟨"$ei"⟩`
|
||
for its bound vars (consistent with `"$y"`/`⟨"$fj"⟩` in `vApp vCompFun`).
|
||
- Five typing theorems proving `idEquiv` components are well-typed in any
|
||
context at their *propositionally-reduced* types (e.g. `sec : A → Path A x x`
|
||
rather than `A → Path A (f (fInv x)) x`). Same compromise as the
|
||
non-dependent Π restriction — β-equivalence isn't part of `HasType`.
|
||
|
||
**Glue** (`Cubical/Glue.lean` + extensions):
|
||
|
||
- New `CType` constructor `.glue (φ : FaceFormula) (T : CType)
|
||
(f fInv sec ret coh : CTerm) (A : CType)` — equivalence's five CTerms
|
||
inlined directly so the `CType`/`CTerm` mutual block remains closed
|
||
(not dependent on `EquivData`).
|
||
- New `CTerm` constructors `.glueIn (φ t a)` and `.unglue (φ f g)`.
|
||
- Threaded through the full pipeline: `CTerm.substDim` extended for both
|
||
new term constructors; `CType.substDim` / `CType.substDimExpr` / all
|
||
four `_aux` mutual defs in `DimLine.lean` extended for `.glue` on the
|
||
CType side and `.glueIn`/`.unglue` on the CTerm side.
|
||
- Two new `CNeu` constructors: `nglueIn` and `nunglue` — structured stuck
|
||
forms preserving both face-on and face-off evaluated sub-values, so
|
||
later dim substitution into `φ` can unstick them.
|
||
- `eval` on `.glueIn` / `.unglue` dispatches by face priority (top/bot/
|
||
stuck), mirrored by six face-disjoint axioms:
|
||
- `eval_glueIn_top` — `.glueIn .top t a → eval t`.
|
||
- `eval_glueIn_bot` — `.glueIn .bot t a → eval a`.
|
||
- `eval_glueIn_stuck` — non-top-non-bot produces `nglueIn`.
|
||
- `eval_unglue_top` — `.unglue .top f g → vApp f g` (forward map).
|
||
- `eval_unglue_bot` — `.unglue .bot f g → eval g` (identity).
|
||
- `eval_unglue_stuck` — non-top-non-bot produces `nunglue`.
|
||
- `EquivData.toGlueType (φ T e A)` — ergonomic wrapper inlining `e`'s
|
||
five CTerms into the `.glue` constructor.
|
||
- `uaLine e A B : DimExpr → CType` — single-face CCHM univalence line
|
||
`Glue [dimExprEq0 r ↦ (A, e)] B`. At `r = .zero`, `dimExprEq0 .zero = .top`,
|
||
so the glue is `A` via `e`; at `r = .one`, `dimExprEq0 .one = .bot`, so
|
||
the glue is `B`. Not a two-face symmetric ua (`Glue [i=0 ↦ (A, e),
|
||
i=1 ↦ (B, idEquiv)] B`); semantically equivalent but simpler.
|
||
- Endpoint rfl-lemmas `uaLine_zero / one / var`, plus computational
|
||
content theorems `uaLine_zero/one_glueIn/unglue_reduces` that derive
|
||
A-behaviour at `r=0` and B-behaviour at `r=1` from the face-disjoint
|
||
axioms.
|
||
|
||
### Correctness audit for Week 5
|
||
|
||
- **Axiom disjointness** — glueIn's three axioms partition by `.top` /
|
||
`.bot` / (non-top ∧ non-bot); unglue's three axioms similarly. No two
|
||
axioms for the same head can fire together.
|
||
- **Structured stucks** — `nglueIn` / `nunglue` preserve sub-values as
|
||
`CVal`s (not erased to neutrals). When dim substitution resolves the
|
||
stored `φ` to `.top` or `.bot`, a later pass can unstick by re-evaluating
|
||
through the endpoint axioms.
|
||
- **`CType.glue` in substitution machinery** — extended for `substDim`,
|
||
`substDimExpr`, `substDim_of_absent`, `substDimExpr_of_absent`,
|
||
`dimAbsent_after_substDim`, and `substDim_comm_aux`. All five threads
|
||
uniformly descend into the 5 inlined CTerms + T + A + φ.
|
||
- **Partial: Glue transport** — a restricted-form axiom
|
||
`eval_transp_glue_const_at_bot` (in `Glue.lean`) covers the case of an
|
||
empty outer face, constant-in-i fiber/base/equivalence components, and
|
||
inner glue face collapsing to `.bot` at `i := 1`. This discharges
|
||
`transp_ua` as a *theorem* (not axiom). The generalisation to varying
|
||
equivalences, varying base type, and non-`.bot` outer faces remains
|
||
Week 6+ work — extending this one axiom into a full CCHM §6.2 rule is
|
||
the first step toward a complete Glue reduction system.
|
||
- **Deferred: multi-face Glue** — a `.glueN` with a list of `(φ_k, T_k, e_k)`
|
||
clauses would let us write the full two-face CCHM ua. Single-face is
|
||
semantically equivalent and enough for Phase 1.
|
||
|
||
### Phase 1 Week 6 (2026-04-23): Glue transport — constant-component fragment
|
||
|
||
**`transp_ua` is now a theorem.** Glue transport is closed for the
|
||
"constant-non-φ-components" case — equivalences supplied from outside the
|
||
transport binder — modulo a single future-work gap (the `φ[i:=1] = .top`
|
||
sub-case, which needs the equivalence's half-adjoint hcomp construction).
|
||
|
||
Changes:
|
||
|
||
- **Eval.lean — `.transp` dispatch on `.glue`.** The `eval` partial def's
|
||
`.transp` arm now has a dedicated `.glue` case that produces a stuck
|
||
neutral as a runtime placeholder (the same shape `vTransp` would have
|
||
produced for glue). This separates `.glue` from the `_ => vTransp`
|
||
fallback so the new Glue-specific axioms don't collide with
|
||
`eval_transp_nonpath`.
|
||
- **Eval.lean — `eval_transp_nonpath` restricted.** Added an `h_not_glue`
|
||
precondition excluding `.glue` type heads. Without this, the new Glue
|
||
axioms would directly contradict `eval_transp_nonpath` + `vTransp_stuck`
|
||
on the `.glue` case. Downstream theorems `eval_transp_pi`,
|
||
`eval_transp_stuck`, and `EvalTest.eval_transp_neu` all propagate the
|
||
precondition.
|
||
- **Glue.lean — `eval_transp_glue_const_at_bot`.** When all non-φ
|
||
components are dim-absent from `i`, the outer face `ψ ≠ .top`, and the
|
||
inner face restricted to `i := 1` collapses to `.bot`, glue transport
|
||
reduces to `transp i A ψ (unglue (φ[i:=0]) f t)`. In the `uaLine`
|
||
setting (constant `A`, constant equivalence, `φ = eq0 i`), this chains
|
||
through `eval_transp_const` (T2) and `eval_unglue_top` to yield `e.f t`.
|
||
- **Glue.lean — `eval_transp_glue_const_stuck`.** When the inner face
|
||
restricted to `i := 1` is neither `.top` nor `.bot` (so the glueIn at
|
||
`i = 1` has a non-trivial face and the reduction can't collapse), the
|
||
result is a structured stuck neutral preserving the full glue data,
|
||
matching the partial def's runtime behavior. Disjoint from
|
||
`_at_bot` by the `φ[i:=1] ≠ .bot` precondition.
|
||
|
||
**Case-analysis status for constant non-φ components:**
|
||
|
||
| `φ.substDim i .one` | status | axiom / mechanism |
|
||
|---------------------|-------------|----------------------------------------------|
|
||
| `.bot` | ✅ covered | `eval_transp_glue_const_at_bot` |
|
||
| `.top` | ✅ covered | `eval_transp_glue_const_at_top` (Stream B #1a, 2026-04-23) |
|
||
| otherwise | ✅ covered | `eval_transp_glue_const_stuck` (stuck form) |
|
||
|
||
**Constant-component fragment is closed.** All three sub-cases of
|
||
`φ.substDim i .one` are face-disjoint axioms with documented discharge
|
||
obligations on the Rust backend. `transp_ua` and the new
|
||
`transp_ua_inverse` (Soundness.lean) demonstrate the `_at_bot` and
|
||
`_at_top` axioms firing through the canonical univalence-line shapes.
|
||
|
||
**New proof obligation on the Rust backend:** the full CCHM §6.2 Glue
|
||
transport formula, whose restriction to each covered sub-case must
|
||
produce the stated RHS. In particular the `_at_top` axiom states the
|
||
T-side witness as the *naïve* `app fInv (transp i A ψ (unglue …))`; the
|
||
full CCHM formula adds an hcomp-in-T correction using `sec` to enforce
|
||
ψ-boundary agreement, which trivialises under the constant-component
|
||
hypothesis up to the equivalence's propositional coherence (a
|
||
well-typedness obligation the caller already discharges).
|
||
|
||
**Remaining Glue-transport generalisations** (require more machinery):
|
||
- Varying base type `A`: replace the outer transport with a genuine
|
||
comp/fill through the base line (CCHM §6.2's first fill).
|
||
- Varying equivalence components `f(i)`, `fInv(i)`, etc.: use
|
||
`transp^i T` to transport witnesses along the equivalence line.
|
||
- Hcomp-correction wrapper around the `_at_top` RHS: requires T-side
|
||
hcomp infrastructure to state the formula precisely.
|
||
|
||
### Phase 1 Week 7 (2026-04-23): step↔eval bridge — Session 1
|
||
|
||
**Goal of the bridge.** The project currently carries two parallel
|
||
axiom sets: step-level (T1–T5, C1/C2/C4, `step_papp_plam`) and eval-level
|
||
(`eval_*` mirrors of the partial def). They must agree but nothing
|
||
enforces this. The bridge defines `step` as a normalisation-by-evaluation
|
||
composition — `step t := readback 0 (eval .nil t)` — so step-level axioms
|
||
become theorems derivable from eval-level axioms alone. Net effect: ~8
|
||
fundamental axioms (step-level) become derivable, and the Rust backend's
|
||
obligation list halves.
|
||
|
||
**Why multiple sessions.** The bridge requires (a) defining readback
|
||
(NbE reification) as a partial def, (b) proving a readback/eval
|
||
correspondence lemma, (c) redefining step and deriving each existing
|
||
step axiom as a theorem. Each piece is substantial; this is a five-session
|
||
plan.
|
||
|
||
**Session 1 (this session) — `Topolei/Cubical/Readback.lean` landed:**
|
||
|
||
- `partial def readback : Nat → CVal → CTerm` and
|
||
`partial def readbackNeu : Nat → CNeu → CTerm` in a mutual block,
|
||
covering every `CVal` and `CNeu` constructor (including the cubical
|
||
closures — `vTranspFun`, `vCompFun`, `vHCompFun`, `vTubeApp`,
|
||
`vPathTransp` — and every neutral shape).
|
||
- Depth-indexed fresh-name generation: `$rb_<n>` for term binders,
|
||
`$rd_<n>` for dim binders. Extends the `$`-prefixed hygiene
|
||
convention already used by the evaluator.
|
||
- Correct α-renaming semantics for `vlam`: extend env at the *original*
|
||
binder name mapped to `vneu (nvar fresh)`, so body lookups return the
|
||
fresh neutral. (First implementation attempt extended at the fresh
|
||
name, yielding `λx. x ↝ λ$rb_0. x` — caught by the test suite.)
|
||
- Face-disjoint reduction axioms mirroring every partial-def arm, same
|
||
pattern as `eval_*` in Eval.lean.
|
||
- `CTerm.readback : CTerm → CTerm := readback 0 ∘ eval .nil` — the
|
||
convenience wrapper that Session 3 will use to redefine step.
|
||
- Three sanity tests in `ReadbackTest`: free variable (`var_readback`),
|
||
identity function (`id_lambda_readback`), constant function
|
||
(`const_fn_readback`). All exercise the axioms end-to-end and verify
|
||
α-renaming works correctly.
|
||
|
||
**Sessions 2–4 delivered (same day):**
|
||
|
||
| Session | Delivered |
|
||
|---------|-----------|
|
||
| 2 ✅ | Switched from depth-indexed fresh-name generation (`$rb_<n>`) to **original-binder preservation** — env shadowing handles capture, which means `readback (eval .nil t) = t` for closed normal forms in the λ-fragment. Axioms simplified (no depth parameter). Correspondence lemmas `readback_nvar`, `CTerm.readback_var`, `CTerm.readback_lam`, `CTerm.readback_plam` prove readback/eval behavior on canonical λ-fragment forms. |
|
||
| 3 ✅ | Derived five step-level axioms as theorems under NbE: `CTerm.readback_papp_plam` (path β), `readback_transp_id` (T1), `readback_transp_const_id` (T2), `readback_comp_full` (C1), `readback_comp_empty` (C2). Each is a one-to-three-step `rw` chain through the corresponding eval-level axiom. |
|
||
| 4 ✅ | Partial T4 coverage: `readback_transp_plam_top` (full face) and `readback_transp_plam_const` (constant line) — the two cases where NbE reduces through. General T4 (arbitrary line/face) and T3/T5/C4 are documented as requiring machinery beyond NbE reification (typing-preservation for T3/C4, face-formula normalisation for T5, richer `vPathTransp` readback for general T4). |
|
||
|
||
**Session 5 (deferred — independent refactor):** The five NbE-derivable
|
||
step axioms are now provably redundant, but physical removal from the
|
||
codebase requires refactoring clients of `step_papp_plam`
|
||
(`step_papp_zero`, `step_papp_one`, `plam_witnesses_boundaries`) and of
|
||
the derived step-level theorems in TransportLaws/CompLaws. That
|
||
refactor is decoupled from the bridge's soundness benefit — the bridge
|
||
works as-is; removal just shortens the source.
|
||
|
||
**Net Week 7 result**: Rust's obligation list is down from "step axioms
|
||
+ eval axioms + readback axioms" to "eval axioms + readback axioms +
|
||
three semantic step axioms (T3, T5, C4) that truly are beyond NbE".
|
||
Five step axioms eliminated from the fundamental obligation list. See
|
||
the updated Axiom discharge obligations table below.
|
||
|
||
## What is done
|
||
|
||
### Cubical core (all 8 steps of TRANSPORT_PLAN.md)
|
||
|
||
| File | Contents | State |
|
||
|------|----------|-------|
|
||
| `Cubical/Interval.lean` | `DimVar`, `DimExpr`, de Morgan algebra, `DimExpr.subst`, `eval_subst`. **Stage 4.1**: `DimExpr.normalize` + `normalize_preserves_eval` + literal-reduction lemmas (`.inv .zero → .one`, `.inv .one → .zero`, double-negation). `deriving Inhabited` added (for FFI `opaque` requirement). | ✅ Stage 4.1 |
|
||
| `Cubical/Face.lean` | `FaceFormula`, face lattice laws, `restrict`, `Entails`, endpoint lemmas. **Stage 4.3**: `FaceFormula.normalize` + `normalize_preserves_eval` covering the meet/join × top/bot absorption laws — ensures deterministic face dispatch for Rust. `deriving Inhabited` added. | ✅ Stage 4.3 |
|
||
| `Cubical/Syntax.lean` | `CType` (`univ`/`pi`/`path`/**`sigma`**/**`glue`**) and `CTerm` (λ-fragment + transp/comp/compN + `glueIn`/`unglue` + **`pair`/`fst`/`snd`**) mutual inductives, `substDim`, **opaque** `step`. **Stage 1.2**: Σ types added. **Stage 4.4**: step-representation decision documented — Rust may implement natively (Option A) or derive as `readback ∘ eval .nil` (Option B); both satisfy the T4 axiom. | ✅ Stages 1.2 + 4.4 |
|
||
| `Cubical/Line.lean` | **NEW (Stage 1.1)**. `DimLine.inv` (reversed line), `DimLine.concat` (CCHM universe-hcomp, axiomatic), `transp_concat` theorem (cells-spec §14 Critical), `transp_concat_const_{left,right}` unit-law corollaries. Explicit Rust-discharge vs Lean-discharge provenance discipline codified. | ✅ Stage 1.1 |
|
||
| `Cubical/ValueTyping.lean` | **NEW (Stage 2.3)**. Semantic typing relations `HasVal : CVal → CType → Prop` / `HasNeu` / `EnvHasType` (opaque stubs, full inductive definition is Lean-discharge future work). Preservation axioms: `eval_preserves_type`, `readback_preserves_type`, `EnvHasType.nil`, consolidated `CTerm.step_preserves_type`. **T3 and C4 are now theorems** derived from `step_preserves_type` — axiom scales O(1) in type-formers, not O(n). | ✅ Stage 2.3 |
|
||
| `Cubical/FFI.lean` | **NEW (Stage 4.6)**. `@[extern]` `opaque` declarations for every Rust cubical-HoTT entry point: `evalRust`, `vAppRust`, `vPAppRust`, `vTranspRust`, `vHCompValueRust`, `vCompAtTermRust`, `vCompNAtTermRust`, `vFstRust`, `vSndRust`, `readbackRust`, `readbackNeuRust`, `stepRust`, `DimExprNormalizeRust`, `FaceFormulaNormalizeRust`. Companion doc: `FFI_DESIGN.md` (memory + marshalling) + `FFI_COMPLETENESS.md` (axiom audit). | ✅ Stage 4.6 |
|
||
| `Cubical/Subst.lean` | `CTerm.substDimBool`, `CType.substDim` (Bool), `CType.substDimExpr` (general DimExpr) — **extended for `.glue`** — `substDim_eq_substDimExpr` bridge, reduction lemmas | ✅ complete |
|
||
| `Cubical/DimLine.lean` | `DimLine`, endpoints `at0`/`at1`, `dimAbsent` (extended for `.glue`/`.glueIn`/`.unglue`), general-DimExpr absent-subst, Bool-case corollaries, `substDim_idem`, `substDim_comm` — all four `_aux` mutual defs extended | ✅ complete |
|
||
| `Cubical/Typing.lean` | `HasType` judgment (var/lam/app/plam/papp/transp/comp), inversion lemmas, `weaken`, `plam_boundaries` | ✅ complete |
|
||
| `Cubical/TransportLaws.lean` | **Stage 2.3**: T3 `transp_step_preserves` is now a *theorem* derived from `CTerm.step_preserves_type` (ValueTyping.lean). **Stage 4.2**: T4 `transp_plam_is_plam` replaced by constructive `transp_plam_is_plam_path` — path-restricted with explicit body `.plam j (.compN i A [(φ, body), (.eq0 j, a), (.eq1 j, b)] body)` mirroring `readback_vPathTransp_plam`. Rust discharges concretely. Derived: `transp_plam_body_path`, `transp_plam_body_path_eq`, `transp_plam_step_typed_path`. | ✅ Stages 2.3 + 4.2 |
|
||
| `Cubical/System.lean` | `System`, `CompatAt0`, compat lemmas, `System.Valid`, `HasType.comp_of_valid` bridge | ✅ complete |
|
||
| `Cubical/CompLaws.lean` | **Stage 2.3**: C4 `comp_step_preserves` is now a *theorem* derived from `CTerm.step_preserves_type`. Zero axioms in this file. C3 intentionally dropped (see file header). | ✅ Stage 2.3 |
|
||
| `Cubical/Value.lean` | Mutual `CEnv` / `CVal` / `CNeu` extended with `nglueIn`/`nunglue` + **`vpair`** (CVal) + **`nfst`/`nsnd`** (CNeu), `CEnv.lookup`/`extend`, `Inhabited` instances. **Stage 1.2**: Σ values added. | ✅ Stage 1.2 |
|
||
| `Cubical/Eval.lean` | Mutual block of 6 partial defs: `eval`, `vApp`, `vPApp`, `vHCompValue`, `vCompAtTerm`, `vCompNAtTerm`. λ-fragment + plam/papp + priority-ordered `.transp` dispatch (top / const / path / **glue (stuck placeholder)** / non-path-non-glue via `vTransp`) + `vCompAtTerm` for `.comp` + `vCompNAtTerm` for `.compN` + face-disjoint dispatch for `.glueIn` (top/bot/stuck) and `.unglue` (top/bot/stuck). `vApp` performs full CCHM Π β-rules on `vTranspFun`, `vHCompFun`, and `vCompFun`. `vPApp` reduces `vPathTransp` at `.zero`/`.one` endpoints via CCHM multi-clause (j=0/j=1) logic and routes generic dims through `vCompNAtTerm`. `eval_transp_nonpath` now carries an `h_not_glue` precondition so the Glue transport axioms (in `Glue.lean`) have an uncontested slot. | ✅ complete |
|
||
| `Cubical/Readback.lean` | Mutual `readback : CVal → CTerm` / `readbackNeu : CNeu → CTerm` as partial defs covering every CVal/CNeu constructor. **Original-binder preservation** — env shadowing provides capture-avoidance without needing fresh names. Face-disjoint `readback_*` / `readbackNeu_*` axioms mirroring every arm; `vPathTransp` arm splits face-disjointly on `.plam` input (Stream B #2c). `CTerm.readback` wrapper for NbE composition. **Seven NbE theorems** (`readback_papp_plam`, `readback_transp_id`, `readback_transp_const_id`, `readback_comp_full`, `readback_comp_empty`, `readback_transp_plam_path`, `readback_transp_face_congr`) derive the corresponding step-level axioms; `readback_transp_plam_general` combines `_top` / `_const` / `_path` to cover every well-typed path-line input shape. Correspondence lemmas for λ-fragment canonical forms. `ReadbackTest` sanity tests. **Step↔eval bridge Sessions 1–4 + Stream B #2b/#2c.** | ✅ Week 7 + Stream B #2b/#2c |
|
||
| `Cubical/Equiv.lean` | Half-adjoint `EquivData` (f/fInv/sec/ret/coh as CTerms), `idEquiv : CType → EquivData`, rfl-projection lemmas, five typing theorems (`hasType_f/fInv/sec_refl/ret_refl/coh_refl_refl`) at propositionally-reduced types | ✅ Week 5 |
|
||
| `Cubical/Glue.lean` | `EquivData.toGlueType` wrapper, `uaLine` (single-face CCHM ua), endpoint rfl-lemmas, computational content theorems, `idEquiv` specialisations. **Glue-transport axiom family (9 total):** 6 constant-equivalence axioms partitioning `{const-A, varying-A} × {bot, top, stuck}` + 2 **hcomp-corrected `_at_top` variants** (Stage 2.1 — full CCHM §6.2 formula with hcomp-in-T using `ret` for definitional ψ-boundary agreement) + 1 **varying-equivalence stuck axiom** (Stage 2.4 — structural stuck form when any of f/fInv/sec/ret/coh varies in i). `eval_transp_glue_const_at_top_from_hcomp` derives the naked form from the hcomp form at ψ = .bot. | ✅ Stages 2.1 + 2.4 |
|
||
| `Cubical/Transport.lean` | `vTransp` (4 priority-ordered arms, unified Π); `vTranspInv` (line reversal); `vTranspInv_const` / `vTranspInv_top` theorems; four `vTransp` axioms + `vTranspLine` wrapper | ✅ Weeks 3–4 (full Π) |
|
||
| `Cubical/EvalTest.lean` | Tests: λ-fragment β, path β, stuck forms, Week 3 transport-reduction cases, Π-const-domain and Π-varying-domain transport β-unfoldings, Week 4 hetero-comp reductions (C1/C2/const-line), Π hcomp β-rule, vTubeApp reductions | ✅ complete |
|
||
|
||
### Soundness theorems (Stages 1.3 + 2.3)
|
||
|
||
| Theorem | Statement | State |
|
||
|---------|-----------|-------|
|
||
| `Soundness.glue_beta` | `eval env (.unglue φ f (.glueIn φ t a)) = eval env a` under overlap `h_overlap : eval env (.app f t) = eval env a` | **Stage 1.3**: promoted from axiom to theorem. Follows from `eval_unglue_of_glueIn`. |
|
||
| `Soundness.glue_eta` | `eval env (.glueIn φ t (.unglue φ f g)) = eval env g` under overlap `h_overlap : eval env t = eval env (.app f g)` | **Stage 1.3**: promoted from axiom to theorem. Follows from `eval_glueIn_of_unglue`. |
|
||
| `Soundness.transp_ua` | transport along `uaLine e A B (.var i)` at ψ = .bot = `vApp (eval e.f) (eval t)` | Proved — uses `eval_transp_glue_const_at_bot`. |
|
||
| `Soundness.transp_ua_inverse` | transport along `uaLine e A B (.inv (.var i))` at ψ = .bot = `vApp (eval e.fInv) (eval t)` | Proved — uses `eval_transp_glue_const_at_top`. |
|
||
| `TransportLaws.transp_step_preserves` (T3) | `HasType Γ t L.at0 → HasType Γ (CTerm.step (.transp L.binder L.body φ t)) L.at1` | **Stage 2.3**: promoted from axiom to theorem. Follows from `HasType.transp` + `CTerm.step_preserves_type`. |
|
||
| `CompLaws.comp_step_preserves` (C4) | `HasType Γ t₀ L.at0 → HasType Γ u L.at1 → (compat) → HasType Γ (CTerm.step (.comp L.binder L.body φ u t₀)) L.at1` | **Stage 2.3**: promoted from axiom to theorem. Follows from `HasType.comp` + `CTerm.step_preserves_type`. |
|
||
| `Line.transp_concat` | `vTranspLine (concat L M h) .bot v = vTranspLine M .bot (vTranspLine L .bot v)` | **Stage 1.1** (cells-spec §14 Critical, previously unstated). Restatement of the eval-level `vTranspLine_concat` axiom. |
|
||
| `Line.transp_concat_const_{left,right}` | unit-law corollaries via T2 | **Stage 1.1**. |
|
||
|
||
### EML and GPU layer
|
||
|
||
| File | Contents | State |
|
||
|------|----------|-------|
|
||
| `EML.lean` | `EMLExpr` AST, GLSL codegen, `PlotConfig`, demo expressions | ✅ complete |
|
||
| `EML/Path.lean` | `boolToFloat`, `EMLExpr.evalWithEnv`, `EMLPath`, `at0`/`at1`, `const_endpoints`, `expPath` example | ✅ complete |
|
||
| `GPU/Spec.lean` | Opaque GPU types, `EMLExpr.evalAt`/`toColor`, `compileEML` + per-handle `compileEML_correct` axiom, `render_faithful` stub, IEEE Float axioms, EMLPath rendering bridge (`evalAt_body_eq_at1`, `toColor_body_eq_at1_toColor`, `render_eq_at1`), `evalAt_expOf` discharged | ✅ complete |
|
||
| `Canvas.lean` | FFI bindings for GLFW/OpenGL (`canvasRun`, `canvasRun2`) | ✅ complete |
|
||
|
||
---
|
||
|
||
## Open sorries
|
||
|
||
None. `evalAt_expOf` is now discharged against three IEEE 754 Float axioms
|
||
(`Float.log_one`, `Float.max_one_ge_eps`, `Float.sub_zero`) that serve as
|
||
verification obligations on the Rust FFI runtime.
|
||
|
||
---
|
||
|
||
## Deferred formal work
|
||
|
||
### Dependent Π types (`Typing.lean`, comment)
|
||
|
||
`HasType` has non-dependent `pi A B` only. Full dependent `(x:A) → B x` requires a
|
||
term evaluator (to apply `B` to a term). Noted as deferred.
|
||
|
||
### Full `φ`-substitution in `CTerm.substDim` (`Syntax.lean`, comment)
|
||
|
||
**RESOLVED** (Phase 1 Week 4++): `CTerm.substDim` now substitutes into face
|
||
formulas via `FaceFormula.substDim`. The approximation note no longer
|
||
applies.
|
||
|
||
---
|
||
|
||
## Phase 1 Week 4+++: Heterogeneous Π composition (fully reduced)
|
||
|
||
The previously-stuck heterogeneous Π comp case now reduces via a
|
||
CCHM-faithful fill-based β-rule.
|
||
|
||
### Machinery
|
||
|
||
- **`CVal.vCompFun : CEnv → DimVar → CType → CType → FaceFormula → CTerm → CTerm → CVal`** —
|
||
closure storing env, line binder, domA, codA, face, and tube/base CTerms.
|
||
- **`vCompAtTerm`**'s `.pi`-with-varying-line arm produces `vCompFun` instead
|
||
of the prior stuck `ncomp` neutral.
|
||
- **`vApp` on `vCompFun`** runs the CCHM fill β-rule:
|
||
- Construct `y_at_i = transp^{fj} (A[i := (inv fj) ∨ i]) φ y` — the fill.
|
||
- Construct `y_at_0 = transp^{fj} (A[i := inv fj]) φ y` — inverse-endpoint.
|
||
- Inner comp: `comp^i B(i) φ (u y_at_i) (t y_at_0)`.
|
||
- Evaluated in env extended with reserved `"$y"` bound to the argument.
|
||
- Reserved names `"$y"` (arg) and `⟨"$fj"⟩` (fill dim) are documented
|
||
hygiene assumptions.
|
||
|
||
### Fill endpoint correctness (verified in comments)
|
||
|
||
- At `fj = 0`: line-slice is `A[i := 1 ∨ i] = A(1)`. Source correct (`y : A(1)`). ✓
|
||
- At `fj = 1`: line-slice is `A[i := 0 ∨ i] = A(i)`. Target correct. ✓
|
||
- At `i = 0`: reduces to the inverse-transport formula. ✓
|
||
- At `i = 1`: line collapses to `A(1)` constant, T2 gives `y` as expected. ✓
|
||
|
||
### Degenerate case: const domain
|
||
|
||
When `dimAbsent i domA = true`, `substDimExpr` is identity, the transport
|
||
line in the fill is constant, T2 fires and the fills reduce to `y`. The
|
||
β-rule collapses to `comp^i B(i) φ (u y) (t y)` — no special-case logic
|
||
needed, just by following the reductions.
|
||
|
||
### Axioms
|
||
|
||
- `eval_comp_pi` — routes `comp^i (pi A B) φ u t` (non-top, non-bot,
|
||
varying line) to `vCompFun`.
|
||
- `vApp_vCompFun` — the full β-rule as an equation.
|
||
- `eval_comp_stuck` gets a new `h_not_pi` precondition (excluded by
|
||
`eval_comp_pi` for any pi-headed type).
|
||
|
||
### Tests
|
||
|
||
- `eval_comp_pi_example` — hetero comp on a varying pi produces `vCompFun`.
|
||
- `vApp_vCompFun_const_dom_example` — const-domain β fires.
|
||
- `vApp_vCompFun_varying_dom_fires` — varying-domain β fires, producing
|
||
the genuinely non-trivial fill terms.
|
||
- Spot-check `CType.substDimExpr` computes the expected composite
|
||
domain type `path univ "a0" (papp "p" ((inv fj) ∨ i))`.
|
||
|
||
---
|
||
|
||
## Axiom discharge obligations
|
||
|
||
These are intentional axioms — formal specs for what the Rust evaluator
|
||
(linked via `@[extern]` + `@[implemented_by]`) must compute. Topolei is
|
||
structured as a Lean 4 extension: the external-to-Lean layer is always
|
||
*specified as axioms in Lean first*, then *discharged* by the Rust module.
|
||
Each becomes a proof obligation when implementation is written.
|
||
|
||
### Cubical evaluator (TransportLaws + CompLaws + Readback)
|
||
|
||
The Phase 1 Week 7 step↔eval bridge promoted five step-level axioms to
|
||
NbE theorems in `Cubical/Readback.lean`. Stream B #2d (Session 5
|
||
cleanup, 2026-04-23) **physically removed** those five axioms and their
|
||
client cascade — they no longer appear as axioms in the Lean source.
|
||
|
||
| Axiom (former) | Obligation | Current status |
|
||
|----------------|------------|----------------|
|
||
| `step_papp_plam` | `step ((⟨i⟩ t) @ r) = t[i := r]` | ✅ Removed; theorem `CTerm.readback_papp_plam` |
|
||
| T1 `transp_id` | `transpⁱ A 1_F t` reduces to `t` | ✅ Removed; theorem `CTerm.readback_transp_id` |
|
||
| T2 `transp_const_id` | transport under constant (i-absent) type is identity | ✅ Removed; theorem `CTerm.readback_transp_const_id` |
|
||
| C1 `comp_full` | `comp ... 1_F u t₀` reduces to `u[i:=1]` | ✅ Removed; theorem `CTerm.readback_comp_full` |
|
||
| C2 `comp_empty` | `comp ... 0_F u t₀` reduces same as `transp ... 0_F t₀` | ✅ Removed; theorem `CTerm.readback_comp_empty` |
|
||
|
||
| Axiom (residual) | Obligation | Status |
|
||
|------------------|------------|--------|
|
||
| T3 `transp_step_preserves` | transport is type-safe (subject reduction) | ⚠️ Stays step-level; needs typing-preservation machinery (Stream B #2a) |
|
||
| T4 `transp_plam_is_plam` | transport of `⟨j⟩ body` produces `⟨j⟩ body'` | ✅ NbE coverage complete for path-typed lines via `readback_transp_plam_general` (combines `_top` / `_const` / `_path`); see `Cubical/Readback.lean`. Non-path varying lines are vacuous in well-typed code (Stream B #2c, 2026-04-23). Step axiom retained for syntactic-only consumers. |
|
||
| T5 `transp_face_congr` | transport inspects face formulas only by truth value | ✅ Promoted to eval-level axiom `eval_transp_face_congr` in `Eval.lean`; NbE theorem `CTerm.readback_transp_face_congr` in `Readback.lean`. Step-level axiom removed (Stream B #2b, 2026-04-23). |
|
||
| C4 `comp_step_preserves` | composition is type-safe | ⚠️ Stays step-level; same typing-preservation gap as T3 |
|
||
|
||
**Remaining true obligations on Rust for the cubical evaluator**:
|
||
- All eval-level equations (mirroring `eval` / `vApp` / `vPApp` / `vTransp`
|
||
/ `vHCompValue` / `vCompAtTerm` / `vCompNAtTerm` arms).
|
||
- All readback-level equations (mirroring `readback` / `readbackNeu` arms).
|
||
- Glue-transport axioms (`eval_transp_glue_const_at_bot` / `_at_top` / `_stuck`).
|
||
- The six glueIn/unglue face-disjoint axioms.
|
||
- The eval-level T5 axiom `eval_transp_face_congr` (face normalisation).
|
||
- The two residual step-level axioms T3 and C4 (subject reduction —
|
||
needs typing-preservation machinery, Stream B #2a).
|
||
- The step-level T4 `transp_plam_is_plam` retained as a syntactic
|
||
fallback (NbE coverage for path-typed lines is complete via
|
||
`readback_transp_plam_general`).
|
||
|
||
*(CCHM C3 — "transport is a specialised composition" — is intentionally
|
||
not stated here; see the note at the top of `CompLaws.lean`. It only holds
|
||
with side-conditions that would duplicate `transp_const_id` in content, and
|
||
will be recovered via the real `transp = hcomp + fill` reduction when the
|
||
evaluator is written in Phase 1 Week 4.)*
|
||
|
||
### Evaluator equations (Eval.lean + Transport.lean + Glue.lean)
|
||
|
||
Axioms mirroring the `partial def` arms of `eval`, `vApp`, `vPApp`:
|
||
`eval_var`, `eval_lam`, `eval_app`, `eval_plam`, `eval_papp`, `eval_transp`,
|
||
`eval_comp`, `vApp_vlam`, `vApp_vneu`, `vPApp_vplam`, `vPApp_vneu`. Plus
|
||
three for `vTransp`: `vTransp_top`, `vTransp_const`, `vTransp_stuck`. All
|
||
will become provable theorems once the evaluator is rewritten as a total
|
||
function with a proper termination measure. `eval_transp_top`,
|
||
`eval_transp_const`, `eval_transp_stuck` are already theorems, derived from
|
||
the corresponding `vTransp_*` axioms.
|
||
|
||
**Glue-transport axioms** (Glue.lean, complete for constant-equivalence
|
||
sub-cases — both constant-A and varying-A; varying-equivalence is Stream
|
||
B #1c, future):
|
||
|
||
| Axiom | A varies? | `φ[i:=1]` | Obligation |
|
||
|-------|-----------|-----------|------------|
|
||
| `eval_transp_glue_const_at_bot` | no | `.bot` | reduces to `transp i A ψ (unglue (φ[i:=0]) f t)` |
|
||
| `eval_transp_glue_const_at_top` | no | `.top` | reduces to `app fInv (transp i A ψ (unglue (φ[i:=0]) f t))` |
|
||
| `eval_transp_glue_const_stuck` | no | residual | structured `ntransp` neutral |
|
||
| `eval_transp_glue_varA_at_bot` | yes | `.bot` | reduces to `compN i A [(ψ, unglue φ f t), (φ, app f t)] (unglue (φ[i:=0]) f t)` |
|
||
| `eval_transp_glue_varA_at_top` | yes | `.top` | reduces to `app fInv (compN i A [(ψ, unglue φ f t), (φ, app f t)] (unglue (φ[i:=0]) f t))` |
|
||
| `eval_transp_glue_varA_stuck` | yes | residual | structured `ntransp` neutral |
|
||
|
||
The six axioms partition `(A.dimAbsent i, φ[i:=1])` into 6 disjoint
|
||
cells — face-disjoint by hypothesis (`hA : … = true / false` and the
|
||
`φ[i:=1]` partition). The Rust backend must reduce CCHM §6.2 Glue
|
||
transport to the matching cell. Hcomp-correction note (Stream B #1a
|
||
refined): the `_at_top` axioms state the principal T-side witness;
|
||
their full CCHM form adds a T-side hcomp using `sec` for ψ-boundary
|
||
agreement, which trivialises under constant-equivalence hypotheses.
|
||
|
||
### GPU layer (GPU/Spec)
|
||
|
||
| Axiom | Obligation |
|
||
|-------|------------|
|
||
| `ShaderHandle.semantic` | opaque semantic function carried by every shader handle |
|
||
| `compileEML` | abstract EML-to-shader compilation function |
|
||
| `compileEML_correct` | `(compileEML expr).semantic = expr.toColor` (scoped per-expr; avoids the earlier unsound universally-quantified version) |
|
||
| `render_faithful` | GPU writes `h.semantic p u` at pixel `p` (needs readback tests; currently `True`) |
|
||
| `Float.log_one` | IEEE 754 `log 1 = 0` |
|
||
| `Float.max_one_ge_eps` | IEEE 754 `max 1.0 1e-9 = 1.0` |
|
||
| `Float.sub_zero` | IEEE 754 `a − 0 = a` for finite `a` |
|
||
|
||
---
|
||
|
||
## Axiom provenance spectrum (Stage 3 audit, 2026-04-23)
|
||
|
||
Every axiom in topolei carries one of three provenance tags that
|
||
determine *where* it is ultimately discharged. The axiom count
|
||
(~100) is large mostly because the Lean side *specifies* the Rust
|
||
evaluator's behaviour one arm at a time; the set of open Lean-side
|
||
proof obligations is small.
|
||
|
||
### A. Rust-discharge axioms (~90 of ~100)
|
||
|
||
These axioms state, arm-by-arm, what the Rust cubical evaluator must
|
||
compute. They cannot be eliminated without changing the partial-def
|
||
structure — they ARE the Lean-as-spec surface of the FFI contract.
|
||
Grouped by file:
|
||
|
||
- `Eval.lean` (48 axioms): one per arm of `eval`, `vApp`, `vPApp`,
|
||
`vHCompValue`, `vCompAtTerm`, `vCompNAtTerm`, `vFst`, `vSnd`. Plus
|
||
the eval-level β/η rules for glue (`eval_unglue_of_glueIn`,
|
||
`eval_glueIn_of_unglue` — Stage 1.3) and T5 face congruence
|
||
(`eval_transp_face_congr` — Stream B #2b).
|
||
- `Transport.lean` (4 axioms): `vTransp_top/_const/_pi/_stuck`.
|
||
- `Readback.lean` (19 axioms): one per arm of `readback` /
|
||
`readbackNeu`, including the face-disjoint `vPathTransp_plam` /
|
||
`_other` pair (Stream B #2c) and the Σ additions (Stage 1.2).
|
||
- `Glue.lean` (9 axioms): the 6 `_const_*` / `_varA_*` face-disjoint
|
||
partition + 2 hcomp-corrected `_at_top` forms (Stage 2.1) + 1
|
||
varying-equivalence stuck axiom (Stage 2.4).
|
||
- `Line.lean` (4 axioms): `DimLine.concat` + `concat_at0` + `concat_at1`
|
||
+ `vTranspLine_concat` (Stage 1.1 — CCHM universe-hcomp discharge).
|
||
- `GPU/Spec.lean` (2–3 axioms): `ShaderHandle` / `GPUContext` opaque
|
||
types, `compileEML` / `compileEML_correct`, `render_faithful`.
|
||
|
||
### B. Lean-discharge axioms (~9 of ~100)
|
||
|
||
These axioms state properties that a future *Lean* development will
|
||
prove. No Rust involved.
|
||
|
||
- `ValueTyping.lean` (4 axioms): `eval_preserves_type`,
|
||
`readback_preserves_type`, `EnvHasType.nil`, `CTerm.step_preserves_type`.
|
||
All discharge once `HasVal` / `HasNeu` / `EnvHasType` are given
|
||
inductive definitions. Unblocks T3 / C4 theorems (already done
|
||
modulo these axioms).
|
||
- `Line.lean` (3 axioms): `DimLine.inv_at0`, `inv_at1`, `inv_inv`.
|
||
Discharge once `DimExpr.normalize` lands (reducing `.inv .zero = .one`
|
||
etc.).
|
||
- `TransportLaws.lean` (1 axiom): T4 `transp_plam_is_plam` — syntactic
|
||
fallback; path-typed inputs are fully NbE-covered. Becomes
|
||
theorem or redundant once a total evaluator is introduced.
|
||
- Reserved for future extension (e.g. Multi-face Glue).
|
||
|
||
### C. IEEE / external axioms (~5 of ~100)
|
||
|
||
- `GPU/Spec.lean` (3 Float axioms): IEEE 754 `log 1 = 0`, `max 1 ε = 1`,
|
||
`a − 0 = a`. Pragmatic; discharged by IEEE 754 specification.
|
||
- Opaque `GPU` types (2).
|
||
|
||
### Key audit findings
|
||
|
||
1. **Only ~9 of ~100 axioms are open Lean-side proof obligations.** The
|
||
rest are either Rust-discharge (intrinsic to the Lean-spec / Rust-
|
||
backend contract) or external-spec. Topolei's formalisation is
|
||
nearly closed at the Lean level.
|
||
|
||
2. **Stage 2.3 consolidation delivered O(1) scaling.** Before: each
|
||
new type-former with a transp/comp rule needed its own
|
||
step-preserves axiom. After: subject reduction scales in the
|
||
single `CTerm.step_preserves_type` axiom — adding new type-formers
|
||
(Σ done, `.glueN` future) automatically inherits preservation.
|
||
|
||
3. **Face-disjoint partitioning is the pervasive pattern.** Recurring
|
||
in Glue transport (9-way partition), glueIn/unglue (3-way + β/η
|
||
redex), transp face cases (4-way), Σ β/neu (2-way). Every axiom
|
||
family consumers' non-overlap by matching/mismatched preconditions.
|
||
This is what lets adding new cases grow the axiom set without
|
||
perturbing proofs that consume existing axioms (e.g. `transp_ua`).
|
||
|
||
4. **Step-level axioms are nearly gone.** After Stages 2.3 + 1.3:
|
||
T1/T2/C1/C2/`step_papp_plam`/T5/glue_β/η are all theorems. T3/C4
|
||
are theorems (via `step_preserves_type`). Only T4
|
||
(`transp_plam_is_plam`) remains — a syntactic fallback that is
|
||
vacuous in well-typed code.
|
||
|
||
---
|
||
|
||
## Missing connections
|
||
|
||
### `Canvas.lean` ↔ formal spec
|
||
|
||
`canvasRun` / `canvasRun2` are FFI calls with no connection to `render_faithful`
|
||
or `compileEML_correct`. No theorem links the window loop to the GPU spec.
|
||
|
||
### `Basic.lean`
|
||
|
||
Currently `def hello := "world"`. Intended role not yet defined.
|
||
|
||
---
|
||
|
||
## Priority order
|
||
|
||
**Phase 1 (Cubical Core) is closed.** Stages 1–3 (post-Phase-1
|
||
refinement, 2026-04-23) are closed: Line primitives, Sigma types,
|
||
β/η-aware glue rules, hcomp-corrected `_at_top`, T3/C4 subject
|
||
reduction, varying-equiv stuck, poet-restructure audit. Three
|
||
mostly-independent work streams remain. Pick by intent, not by
|
||
dependency.
|
||
|
||
**Stream A: Zigzag Engine Lean Port (Phase 2 prerequisite, pure Lean):**
|
||
See `ZIGZAG_PORT.md` for the full 10-step plan. Ports the n-category
|
||
combinatorics (dimension-general normalisation preserving essential
|
||
identities, type-checking against signatures) from Rust reference into
|
||
Lean. Unlocks `Cell/` higher-cell semantics. 6–8 weeks. Rust
|
||
reference at `zigzag-engine/` is for porting from, not a dependency.
|
||
|
||
**Stream B: Pure-Lean extensions of the cubical core.**
|
||
|
||
Each sub-item below is an independent entry point — pick by intent,
|
||
not by dependency. Entry point for each is marked 📍. ✅ items
|
||
landed in Stages 1–3 (2026-04-23).
|
||
|
||
1. **Glue transport — CCHM §6.2 sub-cases.**
|
||
- ✅ **1a. Hcomp-correction wrapper for `_at_top`** (Stage 2.1).
|
||
`eval_transp_glue_const_at_top_hcomp` and `_varA_at_top_hcomp`
|
||
state the full CCHM formula with hcomp-in-T using `ret` for
|
||
definitional ψ-boundary agreement. Naked form derived as theorem
|
||
at ψ = .bot (`eval_transp_glue_const_at_top_from_hcomp`).
|
||
- ✅ **1b. Varying base type `A`** (Stream B #1b).
|
||
- ✅ **1c. Varying-equivalence (structurally stuck)** (Stage 2.4).
|
||
`eval_transp_glue_varEquiv` produces a structured `ntransp`
|
||
neutral when any of `f/fInv/sec/ret/coh` vary in `i`. Full
|
||
reduction formula is future Rust refinement (the axiom becomes
|
||
more informative without Lean-side changes).
|
||
|
||
2. **step↔eval bridge — all residuals now closed or consolidated.**
|
||
- ✅ **2a. T3 / C4 subject reduction** (Stage 2.3). Consolidated into
|
||
`CTerm.step_preserves_type` (ValueTyping.lean); T3 and C4 are
|
||
theorems.
|
||
- ✅ **2b. T5 face congruence** (Stream B #2b).
|
||
- ✅ **2c. General T4 NbE for path-typed lines** (Stream B #2c).
|
||
- ✅ **2d. Physical removal of redundant step axioms** (Stream B #2d).
|
||
- ✅ **Glue β/η on general faces** (Stage 1.3). `glue_beta`,
|
||
`glue_eta` (Soundness.lean) promoted to theorems via eval-level
|
||
`eval_unglue_of_glueIn` / `eval_glueIn_of_unglue`.
|
||
|
||
3. ✅ **Sigma types** (Stage 1.2). `CType.sigma` + `CTerm.pair/.fst/.snd`
|
||
+ value-level `vpair` / `nfst` / `nsnd` + full propagation through
|
||
substDim/dimAbsent/Typing/Value/Eval/Readback. Unlocks Cell.par
|
||
(§6.2), Color triples (§8.1), Shader pair/fst/snd (§9.2).
|
||
`fiberTy` still needs **dependent** Σ (genuinely deferred).
|
||
|
||
4. 📍 **Multi-face Glue** (`.glueN`). *Intentionally deferred.* Enables
|
||
the symmetric two-face ua (cells-spec §5.7); single-face Glue
|
||
(present) is computationally sufficient. Not load-bearing for
|
||
Phase 2 cells. Future work.
|
||
|
||
5. ✅ **Line module** (Stage 1.1). `DimLine.inv` + `DimLine.concat` +
|
||
`transp_concat` theorem (cells-spec §14 Critical, previously
|
||
unstated) + `transp_concat_const_{left,right}` unit-law corollaries.
|
||
Foundation for `Cell.seq` / `Cell.inv` in Phase 2.
|
||
|
||
6. **Hygienic fresh-name generation** (low priority). Current
|
||
convention: user code must avoid `$`-prefixed names. **Entry**:
|
||
add a gensym counter to `CEnv` (or state monad in eval). Can be
|
||
deferred indefinitely.
|
||
|
||
**Stream C: Rust FFI — the one Rust component.**
|
||
|
||
Cubical evaluator backend implementing the eval-level, readback-level,
|
||
and Glue-transport axioms. Linked via `@[extern]` +
|
||
`@[implemented_by]`. Gives Lean's kernel the ability to *reduce*
|
||
cubical terms at native speed. Also hosts the GPU runtime (wgpu) when
|
||
Phase 5 begins. **This is the only Rust component in topolei**; no
|
||
part of the zigzag engine, numerical layer, cell layer, or any other
|
||
phase belongs here.
|
||
|
||
📍 **Entry: minimum Rust obligation list to start implementation** (see
|
||
the Axiom discharge obligations table for the full list):
|
||
|
||
- **Eval arms** (Eval.lean): `eval_var`, `eval_lam`, `eval_app`,
|
||
`eval_plam`, `eval_papp`, the four `eval_transp_*` face-cases, the
|
||
five `eval_comp_*` cases, `eval_compN`, the three `eval_glueIn_*`
|
||
face-cases, the three `eval_unglue_*` face-cases.
|
||
- **vApp / vPApp / vTransp / vHCompValue / vCompAtTerm / vCompNAtTerm
|
||
arms**: as listed in Eval.lean's axiom block.
|
||
- **Readback arms** (Readback.lean): `readback_*` (9 axioms — including
|
||
the face-disjoint `readback_vPathTransp_plam` / `_other` pair) +
|
||
`readbackNeu_*` (9 axioms).
|
||
- **Glue transport** (Glue.lean, 9 axioms after Stages 2.1 + 2.4): the
|
||
six `_const_*` / `_varA_*` face-disjoint axioms + 2 hcomp-corrected
|
||
`_at_top` variants (Stage 2.1) + 1 varying-equivalence stuck axiom
|
||
(Stage 2.4).
|
||
- **Σ eval/readback** (Eval.lean + Readback.lean, Stage 1.2):
|
||
`eval_pair/fst/snd`, `vFst_vpair/vneu`, `vSnd_vpair/vneu`,
|
||
`readback_vpair`, `readbackNeu_nfst/nsnd`.
|
||
- **Line transport** (Line.lean, Stage 1.1): `DimLine.concat` + endpoints
|
||
+ `vTranspLine_concat` (CCHM universe-hcomp).
|
||
- **Glue β/η** (Eval.lean, Stage 1.3): `eval_unglue_of_glueIn`,
|
||
`eval_glueIn_of_unglue`. Soundness-level `glue_beta`/`glue_eta` are
|
||
theorems.
|
||
- **T5 face congruence** (Eval.lean): `eval_transp_face_congr`.
|
||
- **Residual step-level axioms**: only T4 `transp_plam_is_plam`
|
||
(TransportLaws.lean — syntactic fallback). T3 and C4 are theorems
|
||
(via `CTerm.step_preserves_type` in ValueTyping.lean).
|
||
- **Float axioms** (GPU/Spec.lean): `Float.log_one`,
|
||
`Float.max_one_ge_eps`, `Float.sub_zero`.
|
||
- **GPU bridge**: `compileEML`, `compileEML_correct`,
|
||
`render_faithful` (after pixel-readback tests replace `True`).
|
||
|
||
---
|
||
|
||
## Concrete "next session" suggestions (2026-04-23, post-Stages 1–3)
|
||
|
||
With Stages 1–3 closed, the cubical core is minimal and well-shaped.
|
||
Remaining choices are all forward-looking rather than closure-oriented:
|
||
|
||
| Suggestion | Size | Impact |
|
||
|------------|------|--------|
|
||
| **Start Zigzag Lean port** (Stream A) | 6–8 weeks | Unblocks Phase 2 Cell/Higher.lean (n-cells). Independent of cubical-core extensions. |
|
||
| **Start Phase 2 Cell/Basic.lean + Cell/Compose.lean** | 2–3 sessions | Consumes the Line module (Stage 1.1) — `Cell.seq` / `Cell.inv` via `DimLine.concat` / `DimLine.inv`. cell_left_unit / cell_right_unit via `transp_concat_const_{left,right}`. Non-blocked. |
|
||
| **Discharge HasVal / HasNeu inductively** (Stage 2.3 completion) | multi-session | Replaces the four Lean-discharge axioms in ValueTyping.lean with theorems. Unlocks full subject-reduction machinery and dependent typing extensions. |
|
||
| **Multi-face Glue (`.glueN`)** (Stream B #4, deferred) | multi-session | Enables the symmetric two-face ua (cells-spec §5.7). Single-face Glue is sufficient for Phase 2 cells, so this is backlog. |
|
||
| **Varying-equivalence Glue full reduction** (Stage 2.4 refinement) | multi-session | Upgrade `eval_transp_glue_varEquiv` from stuck form to full CCHM computational content (needs `transp^i T` on witness terms). |
|
||
| **`DimExpr.normalize`** | 1–2 sessions | Reduces `.inv .zero = .one` etc. syntactically. Discharges three Lean-side axioms (`DimLine.inv_at0`, `inv_at1`, `inv_inv`). |
|
||
| **Start Rust implementation** (Stream C) | multi-session | **Unblocked by Stage 4.** See `FFI_DESIGN.md` (C ABI contract) + `FFI_COMPLETENESS.md` (per-function axiom audit) + `KERNEL_BOUNDARY.md` (scope contract vs. Lean kernel). `Cubical/FFI.lean` declares the extern entry points; Rust implements them to satisfy the ~90 Rust-discharge axioms. Orthogonal — can run in parallel with any Stream B work. |
|
||
| **Dependent Π / Σ types** | multi-session | Would require a term evaluator to apply `B` to a term. Unlocks proper `fiberTy` (Equiv.lean's "propositionally-reduced types" compromise). |
|