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>
69 KiB
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:
- Architecture at a glance (immediately below) — the Lean-as-host / Rust-as-FFI-backend framing. Skip if already familiar.
- What is done (jump to
## What is doneheading) — current module inventory. - Axiom discharge obligations (
## Axiom discharge obligations) — the live list of what Rust must compute. - Priority order (
## Priority order) — the menu of next-action entry points across Streams A/B/C, with ✅ marks on completed items. - 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_uaand 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:
- 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.
- 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. - 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.
- 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.stepis nowopaque(was a concretedefwith an identity wildcard arm). Every axiom of the formstep (.transp …) = …orstep (.comp …) = …was silently inconsistent withCTerm.noConfusionunder the old definition; opacity restores consistency. The path β-rule is stated as an explicit axiomstep_papp_plam. - CCHM alignment: dropped the non-CCHM axiom
transp_as_comp(T3) and the misleadingtransp_id_from_compderived theorem.comp_top_const_idnow requiresCTerm.dimAbsent i t = trueand is reproved viacomp_full+substDimBool_of_absent, with no appeal to T3. - Dead code: collapsed identical
if j = ibranches inCTerm.substDim(Syntax) andCTerm.dimAbsent(DimLine). Removed tautology theoremssubstDim_false_is_at0/substDim_true_is_at1. - Abstraction: removed useless hypotheses on
comp_full((φ : FaceFormula) (hφ : φ = .top)) andcomp_empty(Entails .bot .bot).comp_full_typednow actually usescomp_full(states typing forsubstDimBool L.binder true u, not forstep).path_at_face_zero/oneremoved (they took unused face hypotheses; the unconditionalCTerm.step_papp_zero/onein Syntax already capture the content).typing_plam_boundariesrenamed toplam_boundariesand stripped of its unused typing hypothesis.System.typed_bot_vacuousrenamed toSystem.typed_bot.
GPU bridge pass
- Soundness (GPU side): the old
compile_correctaxiom universally quantified over bothexprandh, so instantiating with two different exprs at a sharedhforcedexpr₁.toColor = expr₂.toColor. Replaced with an abstract compile functioncompileEML : EMLExpr → ShaderHandleand a per-handle correctness axiomcompileEML_correct. - Rendering bridge: added
EMLPath.evalAt_body_eq_at1(scalar),EMLPath.toColor_body_eq_at1_toColor(color), andrender_eq_at1(end-to-end). These close the cubical → Float → color → pixel chain. - Plot paths: added
PlotConfig.dimNameandPlotConfig.toEMLPathin EML/Path.lean.plotExpandplotLnare proved constant paths (their bodies don't mention"t"); a genuinely parametricplotExpTdemo was added with proved endpoint values. - Last sorry discharged:
evalAt_expOfnow 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: mutualCEnv/CVal/CNeuinductives (named-variable adaptation of the spec's de-BruijnEnv/Val/Neutral).CValcoversvlamclosures,vplamdim-closures, and embeddedCNeu;CNeucovers free variables, stuck (p)apps, and stuck transport/comp.Cubical/Eval.lean: mutualpartial defeval/vApp/vPAppcovering the λ-calculus fragment plus dimension application (β-reduces viaCTerm.substDim). Transport and composition producevneustuck values (real reduction rules are Phase 1 Weeks 3–4). Eleven axiom equations mirror the match arms so theorems can reduce throughevalwithout relying onpartial def's opaque kernel form — same pattern asopaque 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 β viasubstDim, 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: addedCType.substDimExpr : DimVar → DimExpr → CType → CType— generalises the existing Bool-onlyCType.substDimto arbitraryDimExpr. Needed to build the reversed lineA[i := inv i]for inverse transport. Reduction lemmassubstDimExpr_univ/pi/pathand the bridgesubstDim_eq_substDimExprrelating the two.Cubical/DimLine.lean: generalisedCTerm.substDim_of_absentto arbitraryDimExpr(the proof is the same as the old Bool case); addedCType.substDimExpr_of_absent. The oldsubstDimBool_of_absentis now a corollary.
Value-level machinery
Cubical/Value.lean:CVal.vTranspFunnow 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 fromvTranspInv_const.Cubical/Transport.lean:vTransp i A φ vdispatches in four priority-ordered arms:φ = .top→v(T1).CType.dimAbsent i A = true→v(T2).A = pi domA codA→vTranspFun i domA codA φ v(unified full CCHM Π rule — no longer gated bydimAbsent i domA).- 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: whenCType.dimAbsent i A = true, the reversed line equals the original (bysubstDimExpr_of_absent), sovTranspInvreduces tovvia T2. - Theorem
vTranspInv_top:vTranspInv i A .top v = v. - Four axioms:
vTransp_top,vTransp_const,vTransp_pi(new; replaces the earliervTransp_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:vAppon a transported function now performs the full three-step CCHM reduction:
i.e. inverse-transport through the domain, apply the function, forward-transport through the codomain. The axiomvApp (.vTranspFun i domA codA φ f) arg = vTransp i codA φ (vApp f (vTranspInv i domA φ arg))vApp_vTranspFunis updated to match. Derived theoremeval_transp_pireplaces the earliereval_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 producesvTranspFun i .univ codA φ f.vApp_vTranspFun_const_dom_reduces— applying it reduces throughvTranspInv_const(identity) + outervTransp_stuck.eval_transp_pi_varying_dom— varying-domain Π transport also producesvTranspFun(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):
vTranspFunisn't produced; if manually constructed,vAppreduces it tovApp f yvia the two-sidedvTransp_const/vTranspInv_constcollapse. - Constant domain, varying codomain:
vTranspInvreduces to identity, recovering the simpler formulavTransp i codA φ (vApp f arg). - Varying domain: the full CCHM three-step reduction runs; when
pieces stall, they stall consistently into nested
ntransp/nappneutrals. - Under
φ = .top(if manually constructed): reduces tovApp f yviavTransp_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 newCValconstructors + oneCNeu:vHCompFun : CType → FaceFormula → CVal → CVal → CVal— result ofhcomp (pi A B) φ tube baseat the value level. No domain stored (homogeneous comp doesn't transport through the domain).vTubeApp : CVal → CVal → CVal— representsλj. (tube @ j) argas 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 fromncompbecause 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. TakesuandtasCTerms so that thecomp_fullcase can substitutei := 1before evaluating (this is genuinely different fromvPApp-ing the evaluatedu). 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.comparm now routes throughvCompAtTerm.vAppgains avHCompFuncase unfolding per the CCHM Π hcomp rule:vApp (.vHCompFun codA φ tube base) arg = vHCompValue codA φ (.vTubeApp tube arg) (vApp base arg).vPAppgains avTubeAppcase:vPApp (.vTubeApp tube arg) r = vApp (vPApp tube r) arg.- Exhaustiveness cases for
vApponvTubeApp(type error) andvPApponvHCompFun(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 bodypapp (var "p") i; thei := 1substitution hitsDimExpr.subst (var i)which collapses toDimExpr.one, yieldingnpapp (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.univthe hcomp stalls intonhcomp.eval_comp_neu— stuck comp form (free vars, non-const non-endpoint face).vHCompValue_top_reduces—hcomp .topon a vplam tube returns the tube-body at dim 1.vApp_vHCompFun_reduces— CCHM Π hcomp β-rule runs with neutral tube/base/argument; cascades cleanly intonhcomp+nappneutrals.vPApp_vTubeApp_reduces—(λj. tube@j arg) @ rreduces to(tube@r) argusing 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 oftransp^i (Path A(i) a(i) b(i)) φ p. Stores env (for later substDim ona,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.transparm now dispatches in four priority-ordered arms:φ = .top→eval env t(T1, unconditional).CType.dimAbsent i A = true→eval env t(T2, covers constant lines of any head — univ, pi, path, sigma-when-we-have-it).A = .path A₀ a b(line varies) →vPathTranspclosure.- Otherwise → value-level
vTransp(which handles.piviavTranspFun).vPAppgains three new arms forvPathTransp: · 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 …).vAppgains avPathTranspcase (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 linepath univ (var "a_line") (papp (var "b_pt") i)produces the expectedvPathTranspclosure.vPApp_vPathTransp_zero_reduces— endpoint at.zeroyieldsa(1). ExercisessubstDim i .oneon a constant endpoint term.vPApp_vPathTransp_one_reduces— endpoint at.oneyieldsb(1). The.papp (.var "b_pt") (DimExpr.var i)endpoint has itsireplaced with.one, producingnpapp (nvar "b_pt") .one— real CCHM endpoint correction firing through multiple layers.vPApp_vPathTransp_stuck_reduces— at a fresh dim var, stalls atnpathTranspApppreserving all transport data.eval_transp_constant_path— when the path line is fully constant, T2 fires and the transport is identity (novPathTranspproduced).
Correctness audit for path transport
- Axiom disjointness — eval's four
.transpaxioms have preconditions that partition the input space:.top/ (non-top ∧ const) / (non-top ∧ non-const ∧ path) / (non-top ∧ non-const ∧ non-path). Similarly the threevPApp vPathTranspaxioms partition by.zero/.one/else. - Endpoint correctness — the CCHM
(j=0)clause specifies the value atj=0to bea(i); at the lidi=1, this isa(1). Nottransp^i(a(0)), which is what a naïve approach would compute. Our axiom directly gives the CCHM-correct valueeval env (a.substDim i .one). - Constant-line short-circuit — when
dimAbsent i A = trueforA = .path A₀ a b, CCHM transport is identity (T2). Our eval checks this before the path routing, so novPathTranspis constructed and the result is plainlyeval env t. Avoids producing avPathTranspthat would need additional reduction machinery to recognize as identity. - φ = .top short-circuit — similarly, the top face is caught before
path routing, so transport under
.topis identity (T1) for path types as for any other type. NovPathTranspis produced. - Stall preservation — at generic dims, the
npathTranspAppneutral 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)._evaltheorems prove semantic correctness.FaceFormula.substDim : DimVar → DimExpr → FaceFormula → FaceFormula— general substitution. UsesdimExprEq0/1foreq0 j[j:=r]/eq1 j[j:=r].- Supporting theorems:
substDim_zero/substDim_one(agree withrestrict),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.substDimextended forcompNvia a mutual helpersubstDim.clauses(so Lean can see structural termination through the clause list).transpandcompnow substitute intoFaceFormulatoo (were previously no-ops onφ— an approximation that could produce subtle bugs with nested dim substitutions).
Cubical/DimLine.lean:
CTerm.dimAbsentextended forcompN, mutual helperdimAbsent.clauses.CTerm.substDim_absent_aux,CTerm.dimAbsent_after_substDim_aux,CTerm.substDim_comm_auxall extended to handlecompNvia 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.vPathTranspchanged to store the path as aCTerm(not CVal) — needed so.papp p rterms can be constructed for the multi-clause reduction.CNeu.npathTranspAppremoved (no longer produced — path transport now always reduces tovCompNAtTerm, which handles the stuck case itself).
Cubical/Eval.lean:
- Mutual block now has six partial defs:
eval,vApp,vPApp,vHCompValue,vCompAtTerm,vCompNAtTerm. eval's.compNarm delegates tovCompNAtTerm.vPApponvPathTranspat a generic DimExpr now routes throughvCompNAtTermwith the CCHM 3-clause system[(φ, p@r), (dimExprEq0 r, a), (dimExprEq1 r, b)]and basep@r. This is the genuine CCHM path transport reduction, not a stall.vCompNAtTermpriority-order arms: · forcing.topclause anywhere in list → fires (substDim i .one in body). · empty list → reduces to plain transport (C2). · single live clause → delegates tovCompAtTerm. · else → stuckncompN.- New axioms:
eval_compN,vCompNAtTerm_def(compound axiom exposing the full case analysis — users pattern-match on clause list). vPApp_vPathTransp_generalreplaces the oldvPApp_vPathTransp_stuck(now usesvCompNAtTermreduction).
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:dimExprEq0 (inv 0)computes via the de Morgan case todimExprEq1 .zero = .bot.dimExprEq1 (inv 0)computes todimExprEq0 .zero = .top.- compN system
[(φ, _), (.bot, a), (.top, b)]scanned byfind?. .top-faced clause fires → returnsb(1)=b.substDim i .one.pathLine_b.substDim i_dim .one=papp (var "b_pt") .one.- Evaluates to
vneu (npapp (nvar "b_pt") .one). This is real CCHM reduction chaining through every new piece.
Correctness audit for this pass
FaceFormula.substDimpreserves semantic equality withrestrictat endpoints —substDim_zero/one = restrict false/true. Full semantic correctness proven viadimExprEq0_eval/dimExprEq1_evaltheorems (dimExprEq0 revaluates to true iffrevaluates to false).- compN clause-list substitution commutes with disjoint dim vars —
via
CTerm.substDim.clauses_comm_aux(mutual with the CTerm case). .compNdim-absence preserved by substitution — viaCTerm.dimAbsent.clauses_after_substDim(mutual).- Face formulas now substituted in
.transpand.comp— closes a prior gap wheretransp 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) carryingf,fInv,sec,ret,cohas rawCTerms rather than a Σ-type (sigma types aren't inCTypeyet;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"⟩invApp vCompFun).- Five typing theorems proving
idEquivcomponents are well-typed in any context at their propositionally-reduced types (e.g.sec : A → Path A x xrather thanA → Path A (f (fInv x)) x). Same compromise as the non-dependent Π restriction — β-equivalence isn't part ofHasType.
Glue (Cubical/Glue.lean + extensions):
- New
CTypeconstructor.glue (φ : FaceFormula) (T : CType) (f fInv sec ret coh : CTerm) (A : CType)— equivalence's five CTerms inlined directly so theCType/CTermmutual block remains closed (not dependent onEquivData). - New
CTermconstructors.glueIn (φ t a)and.unglue (φ f g). - Threaded through the full pipeline:
CTerm.substDimextended for both new term constructors;CType.substDim/CType.substDimExpr/ all four_auxmutual defs inDimLine.leanextended for.glueon the CType side and.glueIn/.unglueon the CTerm side. - Two new
CNeuconstructors:nglueInandnunglue— structured stuck forms preserving both face-on and face-off evaluated sub-values, so later dim substitution intoφcan unstick them. evalon.glueIn/.ungluedispatches 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 producesnglueIn.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 producesnunglue.
EquivData.toGlueType (φ T e A)— ergonomic wrapper inlininge's five CTerms into the.glueconstructor.uaLine e A B : DimExpr → CType— single-face CCHM univalence lineGlue [dimExprEq0 r ↦ (A, e)] B. Atr = .zero,dimExprEq0 .zero = .top, so the glue isAviae; atr = .one,dimExprEq0 .one = .bot, so the glue isB. 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 theoremsuaLine_zero/one_glueIn/unglue_reducesthat derive A-behaviour atr=0and B-behaviour atr=1from 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/nungluepreserve sub-values asCVals (not erased to neutrals). When dim substitution resolves the storedφto.topor.bot, a later pass can unstick by re-evaluating through the endpoint axioms. CType.gluein substitution machinery — extended forsubstDim,substDimExpr,substDim_of_absent,substDimExpr_of_absent,dimAbsent_after_substDim, andsubstDim_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(inGlue.lean) covers the case of an empty outer face, constant-in-i fiber/base/equivalence components, and inner glue face collapsing to.botati := 1. This dischargestransp_uaas a theorem (not axiom). The generalisation to varying equivalences, varying base type, and non-.botouter 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
.glueNwith 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 —
.transpdispatch on.glue. Theevalpartial def's.transparm now has a dedicated.gluecase that produces a stuck neutral as a runtime placeholder (the same shapevTranspwould have produced for glue). This separates.gluefrom the_ => vTranspfallback so the new Glue-specific axioms don't collide witheval_transp_nonpath. - Eval.lean —
eval_transp_nonpathrestricted. Added anh_not_glueprecondition excluding.gluetype heads. Without this, the new Glue axioms would directly contradicteval_transp_nonpath+vTransp_stuckon the.gluecase. Downstream theoremseval_transp_pi,eval_transp_stuck, andEvalTest.eval_transp_neuall propagate the precondition. - Glue.lean —
eval_transp_glue_const_at_bot. When all non-φ components are dim-absent fromi, the outer faceψ ≠ .top, and the inner face restricted toi := 1collapses to.bot, glue transport reduces totransp i A ψ (unglue (φ[i:=0]) f t). In theuaLinesetting (constantA, constant equivalence,φ = eq0 i), this chains througheval_transp_const(T2) andeval_unglue_topto yielde.f t. - Glue.lean —
eval_transp_glue_const_stuck. When the inner face restricted toi := 1is neither.topnor.bot(so the glueIn ati = 1has 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_botby theφ[i:=1] ≠ .botprecondition.
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.: usetransp^i Tto transport witnesses along the equivalence line. - Hcomp-correction wrapper around the
_at_topRHS: 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 → CTermandpartial def readbackNeu : Nat → CNeu → CTermin a mutual block, covering everyCValandCNeuconstructor (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 tovneu (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 producesvCompFuninstead of the prior stuckncompneutral.vApponvCompFunruns 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.
- Construct
- Reserved names
"$y"(arg) and⟨"$fj"⟩(fill dim) are documented hygiene assumptions.
Fill endpoint correctness (verified in comments)
- At
fj = 0: line-slice isA[i := 1 ∨ i] = A(1). Source correct (y : A(1)). ✓ - At
fj = 1: line-slice isA[i := 0 ∨ i] = A(i). Target correct. ✓ - At
i = 0: reduces to the inverse-transport formula. ✓ - At
i = 1: line collapses toA(1)constant, T2 givesyas 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— routescomp^i (pi A B) φ u t(non-top, non-bot, varying line) tovCompFun.vApp_vCompFun— the full β-rule as an equation.eval_comp_stuckgets a newh_not_piprecondition (excluded byeval_comp_pifor any pi-headed type).
Tests
eval_comp_pi_example— hetero comp on a varying pi producesvCompFun.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.substDimExprcomputes the expected composite domain typepath 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/vCompNAtTermarms). - All readback-level equations (mirroring
readback/readbackNeuarms). - 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_plamretained as a syntactic fallback (NbE coverage for path-typed lines is complete viareadback_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 ofeval,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 ofreadback/readbackNeu, including the face-disjointvPathTransp_plam/_otherpair (Stream B #2c) and the Σ additions (Stage 1.2).Glue.lean(9 axioms): the 6_const_*/_varA_*face-disjoint partition + 2 hcomp-corrected_at_topforms (Stage 2.1) + 1 varying-equivalence stuck axiom (Stage 2.4).Line.lean(4 axioms):DimLine.concat+concat_at0+concat_at1vTranspLine_concat(Stage 1.1 — CCHM universe-hcomp discharge).
GPU/Spec.lean(2–3 axioms):ShaderHandle/GPUContextopaque 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 onceHasVal/HasNeu/EnvHasTypeare given inductive definitions. Unblocks T3 / C4 theorems (already done modulo these axioms).Line.lean(3 axioms):DimLine.inv_at0,inv_at1,inv_inv. Discharge onceDimExpr.normalizelands (reducing.inv .zero = .oneetc.).TransportLaws.lean(1 axiom): T4transp_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 754log 1 = 0,max 1 ε = 1,a − 0 = a. Pragmatic; discharged by IEEE 754 specification.- Opaque
GPUtypes (2).
Key audit findings
-
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.
-
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_typeaxiom — adding new type-formers (Σ done,.glueNfuture) automatically inherits preservation. -
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). -
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 (viastep_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).
-
Glue transport — CCHM §6.2 sub-cases.
- ✅ 1a. Hcomp-correction wrapper for
_at_top(Stage 2.1).eval_transp_glue_const_at_top_hcompand_varA_at_top_hcompstate the full CCHM formula with hcomp-in-T usingretfor 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_varEquivproduces a structuredntranspneutral when any off/fInv/sec/ret/cohvary ini. Full reduction formula is future Rust refinement (the axiom becomes more informative without Lean-side changes).
- ✅ 1a. Hcomp-correction wrapper for
-
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-leveleval_unglue_of_glueIn/eval_glueIn_of_unglue.
- ✅ 2a. T3 / C4 subject reduction (Stage 2.3). Consolidated into
-
✅ 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).fiberTystill needs dependent Σ (genuinely deferred).
- value-level
-
📍 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. -
✅ Line module (Stage 1.1).
DimLine.inv+DimLine.concat+transp_concattheorem (cells-spec §14 Critical, previously unstated) +transp_concat_const_{left,right}unit-law corollaries. Foundation forCell.seq/Cell.invin Phase 2. -
Hygienic fresh-name generation (low priority). Current convention: user code must avoid
$-prefixed names. Entry: add a gensym counter toCEnv(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 foureval_transp_*face-cases, the fiveeval_comp_*cases,eval_compN, the threeeval_glueIn_*face-cases, the threeeval_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-disjointreadback_vPathTransp_plam/_otherpair) +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_topvariants (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+ endpointsvTranspLine_concat(CCHM universe-hcomp).
- Glue β/η (Eval.lean, Stage 1.3):
eval_unglue_of_glueIn,eval_glueIn_of_unglue. Soundness-levelglue_beta/glue_etaare 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 (viaCTerm.step_preserves_typein 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 replaceTrue).
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). |