cubical-transport-hott-lean4/STATUS.md
Maximus Gorog c2e3ecb3e3
Some checks are pending
Lean Action CI / build (push) Waiting to run
Initial commit: topolei — cubical-transport HoTT in Lean 4 + Rust FFI
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>
2026-04-27 20:40:45 -06:00

1163 lines
69 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# Topolei — Formal Status
*Last updated: 2026-04-24 (Phase 1 closed; Stages 14 delivered;
Rust backend Phases AD 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 34). 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 (T1T5, 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 24 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 14 + 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 34 (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` (23 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 13 (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. 68 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 13 (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 13)
With Stages 13 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) | 68 weeks | Unblocks Phase 2 Cell/Higher.lean (n-cells). Independent of cubical-core extensions. |
| **Start Phase 2 Cell/Basic.lean + Cell/Compose.lean** | 23 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`** | 12 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). |