- TRANSPORT_PLAN.md → PHASE1_HISTORY.md with archival header. Path references updated from `Topolei/Cubical/*` to `CubicalTransport/*` to match the post-split namespace. Methodology preserved as a template for future formalisation phases. - ZIGZAG_PORT.md moved here from topolei. This is engine code: the port lands in `CubicalTransport/Zigzag/`, and an AI shortcut on normalisation, degeneracy, or signature-typechecking would cascade-corrupt every higher-cell proof in topolei. Added a cascade caveat header explaining how engine-level zigzag changes ripple back into the topolei interface repo. Body updated for split context. - KERNEL_BOUNDARY.md: clarified that the planned `Eq ↔ Path` bridge module is distinct from the existing trace bridge (`Topolei/Cubical/Trace.lean` in topolei). - README.md: refreshed Reference section with renames + new docs. - native/cubical/README.md: path refs updated. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
13 KiB
Phase 1 History — Transport & Composition Formalization Plan
Archival. This document is the original implementation plan that drove Phase 1 of the cubical-transport HoTT formalization (CCHM §5–6). Every stage listed below is closed: see the
CubicalTransport/*modules for the delivered code andCubicalTest.lean(62/62 passing) for the verification.Paths in the original plan referred to
Topolei/Cubical/*from the pre-split monolithic repo. They have been mechanically updated to the post-splitCubicalTransport/*location, but otherwise the document is preserved as-is — methodology and step decomposition remain useful as a template for future formalization phases (e.g. the zigzag port: seeZIGZAG_PORT.md).
Stage goal (original): formally verify the CCHM transport and composition operations in the deep embedding before writing any Rust — per the project's discipline of stating all external-to-Lean behavior as Lean axioms before the Rust FFI side is built.
Each step has one clear deliverable, depends only on prior steps,
and can be built and checked independently with lake build.
Reference: CCHM §5–6 (Cohen, Coquand, Huber, Mörtberg 2016)
Current state (completed)
| File | Contents |
|---|---|
Interval.lean |
DimVar, DimExpr, de Morgan laws, DimExpr.subst, eval_subst |
Face.lean |
FaceFormula, face laws, restrict, Entails, endpoint lemmas |
Syntax.lean |
CType/CTerm mutual inductives, substDim, step, PathWitness |
Typing.lean |
HasType judgment, inversion lemmas, face-boundary connection |
Step 1 — CType dimension substitution
File: extend Syntax.lean (or new Syntax/Subst.lean)
What: CType needs its own dimension substitution so that a type A
can be evaluated at a specific endpoint of the interval.
Definition:
mutual
def CType.substDim (i : DimVar) (b : Bool) : CType → CType
| .univ => .univ
| .pi A B => .pi (A.substDim i b) (B.substDim i b)
| .path A a t => .path (A.substDim i b)
(a.substDimBool i b) (t.substDimBool i b)
def CTerm.substDimBool (i : DimVar) (b : Bool) : CTerm → CTerm
-- same as substDim with r = (if b then .one else .zero)
end
Note: CTerm.substDimBool i b is definitionally
CTerm.substDim i (if b then .one else .zero).
We may define one in terms of the other to avoid duplication.
Key theorems:
CType.substDim_univ,substDim_pi,substDim_path— reduction lemmasCType.substDim_comm— substituting disjoint dimensions commutesCType.substDim_face— connection toFaceFormula.restrict:(FaceFormula.eq0 i).eval env = true → A.substDim i false = A(at the face, the substitution matches what the face formula asserts)
Acceptance: lake build with no errors; all three reduction lemmas proved.
Step 2 — DimLine: lines of types
File: CubicalTransport/DimLine.lean
What: A line of types λi. A — the domain of transport.
Syntactically, it is a pair of a bound dimension variable and a body type.
This is the A in transpⁱ A φ t.
Definition:
structure DimLine where
binder : DimVar
body : CType
def DimLine.at0 (L : DimLine) : CType := L.body.substDim L.binder false
def DimLine.at1 (L : DimLine) : CType := L.body.substDim L.binder true
def DimLine.atBool (L : DimLine) (b : Bool) : CType := L.body.substDim L.binder b
def DimLine.const (A : CType) (i : DimVar) : DimLine := { binder := i, body := A }
Key theorems:
DimLine.atBool_false:L.atBool false = L.at0DimLine.atBool_true:L.atBool true = L.at1DimLine.const_at0:(DimLine.const A i).at0 = ADimLine.const_at1:(DimLine.const A i).at1 = A(a constant line has the same type at both endpoints — used in Step 5)DimLine.at_face_zero— if(FaceFormula.eq0 L.binder).eval env = truethenL.body.substDim L.binder (env L.binder) = L.at0(the face formula witnesses which endpoint we are at)
Acceptance: lake build; const_at0 and const_at1 proved without sorry.
Step 3 — Extend CTerm with transp and comp; transport typing rule
File: revise Syntax.lean and Typing.lean
What: Add transp and comp as constructors of CTerm now,
so we do not have to reopen the inductive later.
Add the HasType.transp rule in Typing.lean.
New CTerm constructors:
| transp (L : DimLine) (φ : FaceFormula) (t : CTerm) : CTerm
-- transpⁱ (λi.A) φ t
| comp (L : DimLine) (φ : FaceFormula)
(u : CTerm) (t : CTerm) : CTerm
-- compⁱ (λi.A) [φ↦u] t
-- u is the system term; compatibility is enforced in the typing rule
New HasType rule (transport only at this step):
| transp : HasType Γ t L.at0 →
HasType Γ (.transp L φ t) L.at1
comp constructor is added to CTerm here but its typing rule
is deferred to Step 7, after System is defined.
Also: update CTerm.substDim and CTerm.substDimBool to handle
the two new constructors:
| .transp L φ t => .transp (L with body := L.body.substDim i b) φ (t.substDimBool i b)
| .comp L φ u t => .comp (L with body := L.body.substDim i b) φ
(u.substDimBool i b) (t.substDimBool i b)
Acceptance: lake build; HasType.transp rule present;
existing Syntax/Typing theorems still hold.
Step 4 — Transport identity law
File: CubicalTransport/TransportLaws.lean
What: The fundamental CCHM axiom: transport under the trivial
cofibration 1_F is the identity.
Axiom:
axiom transp_id (L : DimLine) (t : CTerm) :
CTerm.step (.transp L .top t) = t
This cannot be proved from the syntax alone — it is a claim about
what the evaluator/kernel must compute. Same pattern as GPU/Spec.lean.
Key theorems derived from transp_id:
transp_id_typed— ift : L.at0and we applytransp_id, thent : L.at1(using the fact thatL.at0 = L.at1when the line is constant — from Step 2)transp_const_id— transport along a constant line is identity regardless ofφ: Proof sketch: constant line hasat0 = at1 = A;transp_idgives the1_Fcase; the generalφcase requirestransp_monotone(below) or is itself axiomatic.transp_monotone(axiom or derived): ifFaceFormula.Entails φ ψthentransp L φ t = transp L ψ t— stronger face → more constrained transport.
Acceptance: transp_id stated as axiom; transp_const_id proved or
stated as axiom with comment explaining which direction is derivable.
Step 5 — Transport–path connection
File: CubicalTransport/TransportLaws.lean (continued)
What: Transport along a constant line recovers path application.
A path p : Path A a b is a term that, when transported with transp,
moves from a to b. This step makes that connection explicit.
Key theorem:
theorem transp_path_apply
(i : DimVar) (p : CTerm) (A : CType) (a b : CTerm)
(hp : HasType Γ p (.path A a b)) :
CTerm.step (.transp (DimLine.const A i) .bot p) =
CTerm.papp p DimExpr.zero
(Transport along a constant line with empty face = path application at 0.
The 1_F direction gives b.)
Also:
path_as_transp— states that every path induces a transport:p : Path A a bgivestransp (const A) _ p : A(the path is itself the transport data)PathWitness_of_transp— if we have a transport that reduces toaatφ = eq0 iand tobatφ = eq1 i, then there is aPathWitnessfor the abstracted term. Connects back toSyntax.lean.
Acceptance: transp_path_apply proved (may use sorry for one
Float/Bool boundary if needed); PathWitness_of_transp stated with
proof or clear sorry marking the missing evaluator.
Step 6 — System: partial elements
File: CubicalTransport/System.lean
What: A system [φ↦u] is a partial term — defined only on
the face where φ holds. This is the new concept that composition
requires beyond transport.
Definition:
structure System where
face : FaceFormula
body : CTerm
/-- Compatibility: the system agrees with base term t₀ on the face φ at i=0.
Formally: whenever φ holds and i=0 holds, body[i:=0] = t₀. -/
def System.CompatAt0 (s : System) (i : DimVar) (t₀ : CTerm) : Prop :=
∀ env : DimVar → Bool,
s.face.eval env = true →
env i = false →
s.body.substDimBool i false = t₀
Key theorems:
System.compat_trivial— the system[1_F↦u]is compatible withu[i:=0]for anyu(trivial face covers everything)System.compat_bot— the empty system[0_F↦u]is compatible with anyt₀(vacuously true; the face never holds)System.compat_meet— ifs₁ands₂are both compatible witht₀, their join system is too (used when combining systems)- Typing rule for system body:
structure System.Typed (Γ : Ctx) (s : System) (L : DimLine) where body_typed : HasType Γ s.body L.at1 -- (body is typed at the *output* end of the line)
Acceptance: compat_trivial and compat_bot proved without sorry.
Step 7 — Composition declaration + typing rule
File: CubicalTransport/Typing.lean (extended)
What: Add HasType.comp — the typing rule for the composition
operation. This is the main event: composition is what makes the
type theory Kan (all cubes have fillers).
New HasType rule:
| comp : HasType Γ t₀ L.at0 →
System.Typed Γ s L →
s.CompatAt0 L.binder t₀ →
HasType Γ (.comp L s.face s.body t₀) L.at1
Reading: given a base term t₀ : L.at0, a system [φ↦u] where
u : L.at1 on the face φ, and a proof that they agree at i=0,
the composition compⁱ (λi.A) [φ↦u] t₀ has type L.at1.
Key theorem:
theorem HasType.comp_inv (h : HasType Γ (.comp L φ u t₀) A) :
A = L.at1 ∧
HasType Γ t₀ L.at0 ∧
∃ s : System, s.face = φ ∧ s.body = u ∧ System.Typed Γ s L
(Inversion: reading off the components of a comp judgment.)
Acceptance: HasType.comp rule compiles; comp_inv proved.
Step 8 — Composition laws
File: CubicalTransport/CompLaws.lean
What: The three fundamental laws of composition. All three are CCHM axioms — not derivable from the syntax.
Axioms:
-- Full system: when the face is 1_F, the system fully determines the result.
axiom comp_full (L : DimLine) (u t₀ : CTerm) :
CTerm.step (.comp L .top u t₀) = u.substDimBool L.binder true
-- Empty system: when the face is 0_F, comp reduces to transport.
axiom comp_empty (L : DimLine) (t₀ : CTerm) :
CTerm.step (.comp L .bot u t₀) =
CTerm.step (.transp L .bot t₀)
-- Transport as special case of composition.
-- (This is the definitional reduction in CCHM.)
axiom transp_as_comp (L : DimLine) (φ : FaceFormula) (t₀ : CTerm) :
CTerm.step (.transp L φ t₀) =
CTerm.step (.comp L φ t₀ t₀)
-- system body = constant t₀; compat holds by substDim idempotence
Key derived theorems:
comp_full_typed— ifHasType.compholds withφ = 1_F, the result equalsu[i:=1]and has the expected typetransp_id_from_comp— re-derivetransp_idfromtransp_as_compandcomp_full(sanity check that the axioms are consistent)comp_absorbs_transp— if a system already covers the whole face, adding a transport does nothing; states the Kan filling property informally
Acceptance: All three axioms stated; transp_id_from_comp
proved as a derived theorem (closes the loop between Steps 4 and 8).
Summary
| Step | File | Deliverable | New sorry? |
|---|---|---|---|
| 1 | Syntax.lean extension |
CType.substDim, reduction lemmas |
No |
| 2 | DimLine.lean |
DimLine, endpoint lemmas, const |
No |
| 3 | Syntax.lean + Typing.lean |
transp/comp constructors, HasType.transp |
No |
| 4 | TransportLaws.lean |
transp_id axiom, transp_const_id |
Axiom (not sorry) |
| 5 | TransportLaws.lean |
transp_path_apply, PathWitness_of_transp |
Maybe 1 |
| 6 | System.lean |
System, CompatAt0, compat lemmas |
No |
| 7 | Typing.lean |
HasType.comp, comp_inv |
No |
| 8 | CompLaws.lean |
Three comp axioms, transp_id_from_comp |
Axioms (not sorry) |
The distinction between axiom and sorry is intentional:
axioms are explicit claims about what the evaluator must satisfy
(same as GPU/Spec.lean); sorry marks genuinely incomplete proofs
that need more work.
Once Step 8 is complete, the cubical core is verified up to the
stated axioms, and the Rust FFI implementation of transport/composition
can begin against those as a formal spec — linked back into Lean via
@[extern] + @[implemented_by] so the kernel can reduce cubical terms
at native speed.