cubical-transport-hott-lean4/cells-spec.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

2391 lines
98 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.

# Cells: A Cubical Transport Calculus for Interactive Computation
## Specification and Rendering Context for Lean 4
**Version:** 0.1.0-draft
**Status:** Foundational specification — no external dependencies
---
## 1. Motivating Principle
### 1.1 Why Not Buffers
Emacs was built on a single primitive: the **buffer** — a mutable sequence of characters with a cursor position, plus metadata. The genius was that everything reduced to this: files, REPLs, shell sessions, mail, version control, games. A buffer is simultaneously the data, the view, and the interaction surface. Its key properties are that it's linear, mutable in place, addressable by position, and composable.
But buffers encode a fundamental assumption: **the primary object of thought is a linear text stream.** That was right for decades. It isn't anymore. The objects people work with now are graphs, not streams — dependency graphs, type hierarchies, dataflow, conversation trees, knowledge graphs, proof trees, UI component trees, API call chains. The operations people want are not "insert character at point" but "transform this structure while preserving these invariants." The feedback loop is not "edit text, run command, read output" but "manipulate a live structure and watch consequences propagate."
### 1.2 The Cell as Primitive
If buffers were the right primitive for the era of linear text, what is the right primitive for the era of structured, interactive, type-aware computation?
A **cell**. But not a spreadsheet cell — a typed, reactive, addressable, composable unit of computation with bidirectional connections. A cell is:
- **A value** of some type (could be text, a function, a type itself, a diagram, a proof term, an image, a running process, a shader).
- **A boundary** — its interface to the outside world, defined by its type. This is what other cells can see and connect to.
- **An interior** — its implementation, which can itself be a network of sub-cells. This is where the fractal self-similarity comes from, analogous to how an Emacs buffer can contain the output of a process that is itself running Emacs.
- **Reactive bindings** — when cells connected to it change, it can recompute. But unlike a spreadsheet, the dataflow graph is first-class and manipulable.
- **A projection** — how the cell renders itself for human interaction. A single cell can have multiple projections (text, graphical, diagrammatic, tabular), and projections are themselves cells.
Just as every Emacs command operates on buffers and returns buffers, every operation in this system operates on cells and returns cells. A cell whose value is a transformation on cells is a macro/command. A cell whose value is a type is a schema/mode. A cell whose value is a projection function is a renderer/view. A cell whose boundary has no free ports is a complete program. A cell whose boundary has free ports is a component/library/API.
The self-referential closure — the equivalent of Emacs being written in Elisp operating on buffers — is that the IDE itself is a cell, its configuration is sub-cells, its commands are cells whose types are endomorphisms on the workspace cell, and modifying the IDE is just refining cells like modifying anything else.
### 1.3 The Foundational Collapse: Cells ARE Transports
The deep realization is that a cell isn't a thing that *has* transport. A cell **is** a transport.
A transport is a map induced by a path in a type family:
```
i : 𝕀 ⊢ A(i) type
tr[i.A(i), 0→1] : A(0) → A(1)
```
A cell from `A` to `B` is a line of types whose endpoints are `A` and `B`. The cell's computation is transport along this line. The cell's rendering is a projection of this line. The cell's interaction surface is the fiber over any point selected along it.
This identification has immediate consequences:
- **Composition** of cells is composition of transports, which is path concatenation.
- **Identity cells** are reflexivity — transport along a constant path, the identity function.
- **Parallel composition** is product transport.
- **The monad laws are the groupoid laws on paths** — associativity of concatenation, identity cancellation — which hold definitionally in cubical type theory.
- **Higher cells are higher transports.** A 2-cell between two cells is a homotopy. A user dragging a slider and watching a shader change continuously is tracing a 2-cell. An n-cell is an n-cube of transports.
The dimension hierarchy:
```
0-cell : a value — a point, a constant, a color
1-cell : a transport — a function, a shader pass, a wire
2-cell : a homotopy — a continuous deformation, a parameter sweep
3-cell : a homotopy of — a second-order variation,
homotopies an animation of an animation
n-cell : ... — full generality
```
### 1.4 The Surreal Direct-Manipulation Vision
The system this specification describes is one where you see a rendered form (a surface, a flow field, a material), grab a point on it (selecting a fiber of the rendering projection), deform it (tracing a path in parameter space), and watch consequences propagate through the cell network at frame rate because the compilation cell emits GPU code. The type of the thing you're manipulating is visible as its shape. The type system prevents ill-typed deformations. The rendering is a projection of the computation, and manipulation of the rendering is transported back to a deformation of the computation.
### 1.5 The Rendering Context as a Cell
The screen is a (discretized) 2-manifold `S`. At each pixel `p ∈ S`, the fiber is the space of possible visual outputs — color, transparency, depth. A rendering context is a section of this fibration — an assignment of visual data to every pixel. Mouse position is a fiber selector: the mouse at `p` evaluates `π⁻¹(p)`, the set of computational cells projecting to that screen point. Clicking selects a section. Dragging transports a section along a path in screen space, lifted back to the computational space.
The fiber doesn't have to be selected by a point. Area selection, lasso, type-based selection — each is a different sub-presheaf of the projection sheaf. The number of distinct generic projections of an n-cell onto the 2D screen is classified by singularity theory (Whitney, Thom-Boardman), and each singularity type — fold curves, cusps, swallowtails — is itself a natural interaction handle in the interface.
### 1.6 Expanding the Cell Space: Pullbacks and Kan Extensions
The cell system begins in a small subspace of all possible computation. External capabilities — GPU APIs, audio engines, physics solvers, network protocols — live in their own type systems. The process of absorbing an external tool into the cell formalism is a **pullback**: finding the universal type that maps into both the cell calculus and the external API, agreeing on shared computational semantics.
But a single pullback doesn't capture the full story. The external tool may have capabilities the cell calculus doesn't yet express (requiring a **right Kan extension** to construct new cell types that map to the external features), and the cell calculus has structure the external tool can't see (transport, composition, higher coherence — a **left Kan extension** in the other direction). The iterative process of absorbing tools is building a diagram of these extensions, and the total system at any point is the **homotopy limit** of the resulting diagram — a Grothendieck fibration whose sections are programs.
### 1.7 The Ground Floor: Hardware and Kernel as Sealed Cells
The registers, ALU operations, pipeline stages, cache hierarchy, kernel syscall interface, and MMU page tables are all cells. But they are cells whose interiors have been sealed — you can see their boundaries (the ISA, the ABI, the syscall convention) but you cannot deform their interiors. They are opaque cells, and the opacity itself is a modal property classified by the accessibility topology.
This is not merely an analogy. The ISA boundary is a sheaf condition: it promises that two different CPUs implementing the same ISA produce the same observable behavior for the same instructions. When this fails — Spectre, Meltdown, Rowhammer — the sheaf condition is violated. A microarchitectural side channel is literally information leaking through a topology that should have hidden it. The cell formalism makes this structure explicit and formal, enabling security analysis as a natural consequence of the mathematical framework rather than an afterthought.
---
## 2. Design Constraints
1. **Zero external HoTT dependencies.** No Ground Zero, no Mathlib, no upstream that can archive or rot. Every line from the interval algebra to the shader pipeline is owned by this project.
2. **Lean 4 kernel compatibility.** Lean 4's kernel has `propext`, `Quot.sound`, and proof irrelevance in `Prop`, which contradicts native univalence. The cubical calculus is therefore **deeply embedded** as data — an `inductive` type family interpreted by a verified evaluator. Lean's equality is never asked to be cubical.
3. **Self-maintainability.** The system must be buildable, testable, and modifiable by a single developer or small team with no dependency on external package ecosystems beyond Lean's `Init` and the Rust standard library.
4. **Practical GPU target.** The cubical formalism must compile, through a verified pipeline, to executable GPU shader code. The proof layer and the execution layer are separated by a narrow FFI boundary.
---
## 3. Architecture Overview — Topolei as a Lean 4 Extension
**Core framing:** Topolei is a Lean 4 project that **extends Lean 4 with cubical-transport HoTT** via a Rust FFI module. Everything — the cubical evaluator, the cell layer, the shader pipeline, the GPU runtime — is *first* formalized in Lean as axiom sets and data structures. The Rust component then discharges those axioms at runtime via `@[extern]` / `@[implemented_by]`, giving the Lean kernel both (a) kernel-speed reduction for cubical terms and (b) access to effects Lean cannot perform natively (GPU, window, OS).
```
┌─────────────────────────────────────────────────────────────┐
│ Pure Lean 4 │
│ │
│ ┌──────────────┐ ┌──────────────┐ ┌─────────────────┐ │
│ │ Cubical │ │ Cell │ │ Shader IR │ │
│ │ Core │──▶│ Graph │──▶│ Compiler │ │
│ │ (+axioms) │ │ & Reactive │ │ (verified) │ │
│ └──────────────┘ └──────────────┘ └────────┬────────┘ │
│ │ │ │
│ │ ── every axiom is a proof obligation ─┤ │
│ ▼ ▼ │
├────────────────────────────────────────────────────────────┤
│ FFI Boundary (C ABI) │
├────────────────────────────────────────────────────────────┤
│ Rust (FFI backend) │
│ │
│ ┌──────────────┐ ┌──────────────┐ ┌─────────────────┐│
│ │ Cubical │ │ GPU │ │ Shader ││
│ │ Evaluator │ │ Runtime │◀──│ Emitter ││
│ │ (axiom │ │ (wgpu / │ │ (SPIR-V / ││
│ │ discharge) │ │ Vulkan) │ │ WGSL bytes) ││
│ └──────────────┘ └──────────────┘ └─────────────────┘│
└────────────────────────────────────────────────────────────┘
```
**Two distinct roles of the Rust layer:**
1. **Cubical evaluator backend** — Rust implements `eval`, `vApp`, `vPApp`, `vTransp`, `vHCompValue`, `vCompAtTerm`, `vCompNAtTerm`, `readback`, `readbackNeu`, and the face-disjoint reduction rules for transport, composition, and glue. Each Rust function is linked via `@[extern]` + `@[implemented_by]` to the matching Lean axiom in `Cubical/Eval.lean`, `Cubical/Readback.lean`, `Cubical/Glue.lean`, and the (residual) `TransportLaws.lean` / `CompLaws.lean` / `Soundness.lean`. Effect: Lean's kernel *reduces* cubical terms, not just *reasons* about them. (`CTerm.step` is defined as `readback 0 ∘ eval .nil` via the Phase 1 Week 7 step↔eval bridge — see `STATUS.md`; Rust therefore does not implement step separately.)
2. **Effectful runtime** — GPU context, shader upload, uniforms, render, window/input, OS primitives. Effects Lean fundamentally cannot perform; their interface specs live in `GPU/Spec.lean` + the Phase 5 Runtime modules as IO axioms.
**Invariant:** Proof obligations remain in Lean. Performance and effect obligations go to Rust. The FFI boundary carries opaque types whose invariants are stated and verified in Lean but whose implementations are in Rust.
**Process discipline:** Every phase formalizes its external-to-Lean needs as Lean axioms *before* the Rust side is written. Phase 1 (Cubical Core) exemplifies this: the eval-level equations (`eval_*`, `vApp_*`, `vPApp_*`, `vTransp_*`, …), the readback equations (`readback_*`, `readbackNeu_*`), the face-disjoint glueIn/unglue/Glue-transport axioms, and the three IEEE Float axioms are all stated in Lean with zero Rust code present. The Rust module, when built, turns this axiom set into a satisfied contract. Five step-level axioms (T1, T2, C1, C2, `step_papp_plam`) are now Lean theorems via the Week 7 bridge and are not separate Rust obligations. Four remain on Rust's plate: T3 and C4 (subject reduction — need typing-preservation machinery), T5 (face congruence — needs face normalisation), and the general case of T4 (plam-shape preservation — needs richer `vPathTransp` readback). See STATUS.md § Week 7 for the per-axiom table.
---
## 4. Package Structure
```
cells/
├── lakefile.lean
├── lean-toolchain
├── Cells.lean -- root import
├── Cells/
│ ├── Cubical/ -- Phase 1: core calculus
│ │ ├── Interval.lean -- de Morgan algebra 𝕀
│ │ ├── Face.lean -- face formulas φ
│ │ ├── Syntax.lean -- CType, CTerm mutual inductive
│ │ ├── Subst.lean -- substitution on terms and dims
│ │ ├── Value.lean -- normal forms / values
│ │ ├── Eval.lean -- evaluator / normalizer
│ │ ├── Transport.lean -- transp for each type former
│ │ ├── Comp.lean -- hcomp / composition
│ │ ├── Glue.lean -- Glue types, univalence
│ │ ├── Equiv.lean -- equivalences, fibers
│ │ └── Soundness.lean -- metatheorems
│ │
│ ├── Cell/ -- Phase 2: cells-as-transports
│ │ ├── Basic.lean -- Cell structure
│ │ ├── Compose.lean -- sequential and parallel comp
│ │ ├── Higher.lean -- 2-cells, n-cells
│ │ ├── Fiber.lean -- fiber selection, sections
│ │ ├── Projection.lean -- projections to lower dims
│ │ └── Graph.lean -- cell networks / workspaces
│ │
│ ├── Reactive/ -- Phase 3: incremental propagation
│ │ ├── Depend.lean -- dependency tracking
│ │ ├── Incremental.lean -- incremental re-typechecking
│ │ └── Propagate.lean -- change propagation through graph
│ │
│ ├── Color/ -- Phase 3b: color as a cell
│ │ ├── Space.lean -- color space types
│ │ ├── Transport.lean -- gamut mapping as transport
│ │ └── Blend.lean -- compositing algebra
│ │
│ ├── Shader/ -- Phase 4: verified compilation
│ │ ├── IR.lean -- shader intermediate representation
│ │ ├── Compile.lean -- CTerm → ShaderIR (verified)
│ │ ├── Optimize.lean -- algebraic simplification
│ │ ├── Emit.lean -- ShaderIR → GLSL string (Lean side)
│ │ └── Numeric.lean -- numerical method cells
│ │
│ ├── Runtime/ -- Phase 5: GPU + interaction
│ │ ├── GPU.lean -- opaque types + FFI declarations
│ │ ├── Input.lean -- input event types
│ │ ├── Window.lean -- window management
│ │ └── Loop.lean -- render-input-deform cycle
│ │
│ ├── Boundary/ -- Phase 5b: accessibility + security
│ │ ├── Access.lean -- AccessLevel, ModalCell, CellEvidence
│ │ ├── Guard.lean -- GuardCell, BoundaryWire, SecurityPolicy
│ │ ├── ProCell.lean -- ProCell, CellSpec, SpecSource
│ │ ├── Classifier.lean -- boundary classifier as a cell
│ │ ├── Hardware.lean -- RegisterCell, ALU ops, sealed tower
│ │ ├── Kernel.lean -- KernelCell, PageTableCell, syscalls
│ │ ├── Security.lean -- vulnerability types, security theorems
│ │ └── Expansion.lean -- ToolIntegration, Grothendieck construction
│ │
│ └── Meta/ -- Phase 6: self-hosting
│ ├── DSL.lean -- syntax extensions for cell notation
│ ├── Tactic.lean -- custom tactics (transport, compose)
│ └── Widget.lean -- editor-as-cell-network
├── native/ -- Rust crate (linked via C ABI FFI)
│ ├── Cargo.toml
│ ├── include/
│ │ ├── cells/gpu.h
│ │ ├── cells/emit.h
│ │ └── cells/numeric.h
│ ├── src/
│ │ ├── gpu/
│ │ │ ├── context.cpp -- GL/Vulkan context lifecycle
│ │ │ ├── shader.cpp -- shader compilation + linking
│ │ │ ├── framebuffer.cpp -- FBO management
│ │ │ └── input.cpp -- GLFW input polling
│ │ ├── emit/
│ │ │ ├── glsl.cpp -- ShaderIR → GLSL 4.50
│ │ │ ├── wgsl.cpp -- ShaderIR → WGSL (WebGPU)
│ │ │ └── spirv.cpp -- ShaderIR → SPIR-V (Vulkan)
│ │ └── numeric/
│ │ ├── blas.cpp -- OpenBLAS wrapper
│ │ ├── mesh.cpp -- mesh generation / refinement
│ │ └── solver.cpp -- PDE discretization kernels
│ └── deps/ -- vendored headers (GLFW, glad, etc.)
├── tests/
│ ├── Cubical/ -- unit tests for evaluator
│ ├── Cell/ -- cell composition tests
│ ├── Shader/ -- compilation round-trip tests
│ ├── Boundary/ -- access level, guard, security tests
│ └── Integration/ -- end-to-end render tests
└── examples/
├── HelloCell.lean -- minimal: one cell, one shader, one window
├── Gradient.lean -- color gradient as transport
├── NoiseField.lean -- procedural noise as cell network
└── Interactive.lean -- mouse-driven deformation
```
---
## 5. Cubical Core — Detailed Specification
### 5.1 The Interval (de Morgan Algebra)
The interval `𝕀` is a bounded distributive lattice with an involution. It is represented as a free algebra on named dimension variables.
```lean
-- Cells/Cubical/Interval.lean
/-- Dimension expressions: the free de Morgan algebra
on dimension variables. -/
inductive Dim : Type where
| i0 : Dim -- the endpoint 0
| i1 : Dim -- the endpoint 1
| var : Nat → Dim -- dimension variable (de Bruijn level)
| meet : Dim → Dim → Dim -- min(r, s) = r ∧ s
| join : Dim → Dim → Dim -- max(r, s) = r s
| inv : Dim → Dim -- 1 - r
deriving Repr, BEq, Hashable
namespace Dim
/-- Substitute dimension variable `n` with value `val`. -/
def subst (n : Nat) (val : Dim) : Dim → Dim
| .i0 => .i0
| .i1 => .i1
| .var m => if m == n then val else .var m
| .meet a b => .meet (a.subst n val) (b.subst n val)
| .join a b => .join (a.subst n val) (b.subst n val)
| .inv a => .inv (a.subst n val)
/-- Evaluate to a Boolean when all variables are instantiated. -/
def eval : Dim → Option Bool
| .i0 => some false
| .i1 => some true
| .var _ => none
| .meet a b => do pure ((← a.eval) && (← b.eval))
| .join a b => do pure ((← a.eval) || (← b.eval))
| .inv a => do pure (!(← a.eval))
/-- Normalize: apply de Morgan laws, double negation, absorption. -/
def normalize : Dim → Dim
| .inv (.inv a) => normalize a
| .inv .i0 => .i1
| .inv .i1 => .i0
| .inv (.meet a b) => .join (.inv (normalize a)) (.inv (normalize b))
| .inv (.join a b) => .meet (.inv (normalize a)) (.inv (normalize b))
| .meet a .i0 => .i0
| .meet .i0 a => .i0
| .meet a .i1 => normalize a
| .meet .i1 a => normalize a
| .join a .i0 => normalize a
| .join .i0 a => normalize a
| .join a .i1 => .i1
| .join .i1 a => .i1
| .meet a b => .meet (normalize a) (normalize b)
| .join a b => .join (normalize a) (normalize b)
| .inv a => .inv (normalize a)
| d => d
end Dim
```
**Laws that must hold (proved as theorems):**
| Law | Statement |
|-----|-----------|
| Idempotence | `meet r r = r`, `join r r = r` |
| Commutativity | `meet r s = meet s r` |
| Associativity | `meet r (meet s t) = meet (meet r s) t` |
| Absorption | `meet r (join r s) = r` |
| Distributivity | `meet r (join s t) = join (meet r s) (meet r t)` |
| De Morgan | `inv (meet r s) = join (inv r) (inv s)` |
| Involution | `inv (inv r) = r` |
| Boundary | `meet r (inv r)` is NOT required to be `i0` (CCHM style) |
### 5.2 Face Formulas
Face formulas are propositions about dimension variables. They define the "boundary" of a cube — the faces on which partial elements are defined.
```lean
-- Cells/Cubical/Face.lean
/-- A face formula: a proposition constraining dimensions. -/
inductive Face : Type where
| eq0 : Nat → Face -- variable i = 0
| eq1 : Nat → Face -- variable i = 1
| and : Face → Face → Face -- φ ∧ ψ
| or : Face → Face → Face -- φ ψ
| top : Face -- (always satisfied)
| bot : Face -- ⊥ (never satisfied)
deriving Repr, BEq
namespace Face
/-- Evaluate a face formula given a dimension assignment. -/
def eval (assign : Nat → Option Bool) : Face → Option Bool
| .eq0 n => do let v ← assign n; pure (v == false)
| .eq1 n => do let v ← assign n; pure (v == true)
| .and φ ψ => do pure ((← φ.eval assign) && (← ψ.eval assign))
| .or φ ψ => do pure ((← φ.eval assign) || (← ψ.eval assign))
| .top => some true
| .bot => some false
/-- Check if one face implies another (conservative). -/
def implies (φ ψ : Face) : Bool :=
sorry -- decision procedure for propositional lattice
/-- The face generated by a dimension being an endpoint. -/
def ofDim (r : Dim) : Face :=
match r.normalize with
| .i0 => .top
| .i1 => .top
| .var n => .or (.eq0 n) (.eq1 n) -- tautology: i=0 i=1
| _ => .bot -- not a simple constraint
end Face
/-- A system of partial elements: values defined on faces.
On overlapping faces, values must agree (the coherence condition). -/
structure System (α : Type) where
clauses : List (Face × α)
```
### 5.3 Syntax — Types and Terms
The core type theory, represented as mutually inductive data types.
```lean
-- Cells/Cubical/Syntax.lean
/-- Universe levels. -/
abbrev Level := Nat
mutual
/-- Types in the cubical calculus. -/
inductive CType : Type where
| univ : Level → CType
| el : CTerm → CType -- El(a) where a : Type_n
| pi : CType → CType → CType -- Π(x : A). B
| sigma : CType → CType → CType -- Σ(x : A). B
| pathTy : CType → CTerm → CTerm → CType -- Path_A(a, b)
| glue : Face → CType → CTerm → CType → CType
-- Glue [φ ↦ (T, e)] A
-- On face φ: type is T with equiv e : T ≃ A
-- Off face φ: type is A
deriving Repr
/-- Terms in the cubical calculus. -/
inductive CTerm : Type where
-- Standard λ-calculus
| var : Nat → CTerm -- de Bruijn index
| lam : CTerm → CTerm -- λx. t
| app : CTerm → CTerm → CTerm -- f a
| pair : CTerm → CTerm → CTerm -- (a, b)
| fst : CTerm → CTerm -- π₁
| snd : CTerm → CTerm -- π₂
-- Type annotation
| ann : CTerm → CType → CTerm -- (t : A)
-- Dimension abstraction and application
| dimLam : CTerm → CTerm -- λ(i : 𝕀). t
| dimApp : CTerm → Dim → CTerm -- t @ r
-- Cubical operations
| transp : CTerm → Face → CTerm → CTerm
-- transp (i. A(i)) φ u₀ : A(1)
-- Precondition: when φ = , A is constant in i
-- and transp computes to identity
-- u₀ : A(0)
| hcomp : CType → Face → CTerm → CTerm → CTerm
-- hcomp A [φ ↦ u] u₀ : A
-- u : (i : 𝕀) → Partial φ A (the tube)
-- u₀ : A (the base)
-- u₀ agrees with u(i0) on φ
-- Glue introduction and elimination
| glueIn : Face → CTerm → CTerm → CTerm
-- glue [φ ↦ t] a : Glue [φ ↦ (T, e)] A
| unglue : Face → CTerm → CTerm
-- unglue φ g : A
-- Literals for base types (shader-relevant)
| floatLit : Float → CTerm
| vecLit : Array Float → CTerm -- vec2, vec3, vec4
| boolLit : Bool → CTerm
| intLit : Int → CTerm
deriving Repr
end
```
### 5.4 Values and Evaluation
The evaluator reduces `CTerm` to `Val` (weak head normal form). This is where the computational content of cubical type theory lives.
```lean
-- Cells/Cubical/Value.lean
/-- An environment: a list of values for bound variables. -/
abbrev Env := Array Val
mutual
/-- Values: weak-head normal forms. -/
inductive Val : Type where
| vlam : Env → CTerm → Val -- closure
| vdimLam : Env → CTerm → Val -- dim closure
| vpair : Val → Val → Val
| vpi : Val → Env → CTerm → Val -- Π type value
| vsigma : Val → Env → CTerm → Val -- Σ type value
| vpathTy : Val → Val → Val → Val -- Path type value
| vglue : Face → Val → Val → Val → Val -- Glue type value
| vuniv : Level → Val -- Type_n
| vneutral : Neutral → Val -- stuck term
| vfloat : Float → Val
| vvec : Array Float → Val
| vbool : Bool → Val
| vint : Int → Val
deriving Repr
/-- Neutral terms: stuck computations. -/
inductive Neutral : Type where
| nvar : Nat → Neutral
| napp : Neutral → Val → Neutral
| nfst : Neutral → Neutral
| nsnd : Neutral → Neutral
| ndimApp : Neutral → Dim → Neutral
| ntransp : Val → Face → Neutral → Neutral
| nhcomp : Val → Face → Val → Neutral → Neutral
deriving Repr
end
```
```lean
-- Cells/Cubical/Eval.lean
/-- Evaluate a term in an environment. -/
partial def eval (env : Env) : CTerm → Val
| .var n => env[n]!
| .lam body => .vlam env body
| .app f a => vApp (eval env f) (eval env a)
| .pair a b => .vpair (eval env a) (eval env b)
| .fst t => vFst (eval env t)
| .snd t => vSnd (eval env t)
| .dimLam body => .vdimLam env body
| .dimApp t r => vDimApp (eval env t) r
| .transp line φ base =>
vTransp (eval env line) φ (eval env base)
| .hcomp A φ tube base =>
vHComp (evalType env A) φ (eval env tube) (eval env base)
| .glueIn φ t a => vGlueIn φ (eval env t) (eval env a)
| .unglue φ g => vUnglue φ (eval env g)
| .floatLit f => .vfloat f
| .vecLit v => .vvec v
| .boolLit b => .vbool b
| .intLit i => .vint i
| .ann t _ => eval env t
/-- Apply a value to an argument. -/
partial def vApp : Val → Val → Val
| .vlam env body, arg => eval (env.push arg) body
| .vneutral n, arg => .vneutral (.napp n arg)
| _, _ => panic! "vApp: not a function"
/-- Project first component. -/
partial def vFst : Val → Val
| .vpair a _ => a
| .vneutral n => .vneutral (.nfst n)
| _ => panic! "vFst: not a pair"
/-- Project second component. -/
partial def vSnd : Val → Val
| .vpair _ b => b
| .vneutral n => .vneutral (.nsnd n)
| _ => panic! "vSnd: not a pair"
```
### 5.5 Transport
Transport is the central computational operation. For each type former, transport has specific behavior.
```lean
-- Cells/Cubical/Transport.lean
/-- Transport along a line of types.
transp (i. A(i)) φ u₀ : A(1)
When φ = , A is constant and transp is the identity.
Otherwise, case-split on the head constructor of A. -/
partial def vTransp (lineA : Val) (φ : Face) (u₀ : Val) : Val :=
-- If φ is satisfied, A is constant → identity
if φ == .top then u₀
else match lineA with
--
-- Π-type: transp contravariantly in domain, covariantly in codomain
-- transp (i. Π(x : A(i)). B(i,x)) φ f
-- = λ(x₁ : A(1)). transp (i. B(i, fill⁻¹(i,x₁))) φ (f (transp⁻¹ x₁))
--
| .vpi domLine codLine => sorry -- implemented per CCHM
--
-- Σ-type: transport componentwise with correction term
-- transp (i. Σ(x : A(i)). B(i,x)) φ (a₀, b₀)
-- = (a₁, transp (i. B(i, fill(i,a₀))) φ b₀)
-- where a₁ = transp (i. A(i)) φ a₀
--
| .vsigma fstLine sndLine => sorry -- implemented per CCHM
--
-- Path type: adjust endpoints
-- transp (i. Path (A(i)) (a(i)) (b(i))) φ p
-- = λ(j : 𝕀). comp (i. A(i)) [j=0 ↦ a(i), j=1 ↦ b(i)] (p @ j)
--
| .vpathTy lineA' lineA0 lineA1 => sorry -- implemented per CCHM
--
-- Glue type: the key case giving univalence
-- Uses the equivalence to transport on the base,
-- then adjusts the fibers
--
| .vglue ψ lineT lineE lineBase => sorry -- implemented per CCHM
--
-- Universe: transp (i. Type) φ A = A
-- (transport in the universe is the identity because
-- univalence is given by Glue, not by transport in Type)
--
| .vuniv _ => u₀
--
-- Neutral: stuck, produce neutral transport
--
| .vneutral n => .vneutral (.ntransp lineA φ (.nvar 0 /- placeholder -/))
| _ => panic! "vTransp: unexpected type"
```
### 5.6 Composition (hcomp)
Homogeneous composition fills open boxes at a fixed type.
```lean
-- Cells/Cubical/Comp.lean
/-- Homogeneous composition.
hcomp A [φ ↦ u] u₀ : A
Given:
A : Type (fixed)
φ : Face (the constrained face)
u : (i : 𝕀) → Partial φ A (the tube: walls of the box)
u₀ : A (the base, agreeing with u(0) on φ)
Produces:
the lid of the box at i = 1
-/
partial def vHComp (tyA : Val) (φ : Face) (tube : Val) (base : Val) : Val :=
if φ == .top then
-- Tube covers everything → just evaluate tube at i=1
vDimApp tube .i1
else match tyA with
| .vpi _ _ => sorry -- per CCHM: pointwise hcomp in codomain
| .vsigma _ _ => sorry -- per CCHM: componentwise with correction
| .vpathTy _ _ _ => sorry -- per CCHM: becomes 2-dimensional comp
| .vglue _ _ _ _ => sorry -- per CCHM: the hard case
| .vneutral n => .vneutral (.nhcomp tyA φ tube (.nvar 0))
| _ => panic! "vHComp: unexpected type"
/-- Heterogeneous composition (derived from transp + hcomp).
comp (i. A(i)) [φ ↦ u] u₀ : A(1)
= hcomp A(1) [φ ↦ transp (j. A(j)) u(i)] (transp (i. A(i)) u₀)
-/
def vComp (lineA : Val) (φ : Face) (tube : Val) (base : Val) : Val :=
sorry -- derived operation
```
### 5.7 Glue Types and Univalence
Glue types are the mechanism by which univalence becomes a theorem rather than an axiom.
```lean
-- Cells/Cubical/Glue.lean
/-- Glue type formation.
Glue [φ ↦ (T, e)] A
On face φ: the type is T, with e : T ≃ A witnessing equivalence.
Off face φ: the type is A.
The boundary is "glued" by the equivalence.
-/
/-- glue introduction: given t : T on face φ and a : A off face φ,
with e(t) = a on the overlap, produce a term of Glue type. -/
partial def vGlueIn (φ : Face) (t : Val) (a : Val) : Val :=
if φ == .top then t -- on the face, it's just t
else if φ == .bot then a -- off the face, it's just a
else sorry -- general case
/-- unglue: extract the A-component from a Glue term. -/
partial def vUnglue (φ : Face) (g : Val) : Val :=
if φ == .top then
sorry -- apply the equivalence: e(g)
else g -- off the face, g is already in A
/-- The univalence map: given e : A ≃ B, construct Path Type A B.
ua(e) := λ(i : 𝕀). Glue [i=0 ↦ (A, e), i=1 ↦ (B, idEquiv)] B
At i=0: Glue [ ↦ (A, e)] B = A (via e)
At i=1: Glue [ ↦ (B, id)] B = B (trivially)
-/
def ua (equiv : CTerm) (tyA tyB : CType) : CTerm :=
.dimLam sorry -- the Glue line described above
/-- Theorem: ua computes correctly at endpoints. -/
theorem ua_zero (e : CTerm) (A B : CType) :
evalType [] (substDim (ua e A B) .i0) = evalType [] A :=
sorry
theorem ua_one (e : CTerm) (A B : CType) :
evalType [] (substDim (ua e A B) .i1) = evalType [] B :=
sorry
/-- Theorem: transport along ua(e) computes as e.
This is the computational content of univalence. -/
theorem transp_ua (e : CTerm) (a : CTerm) :
eval [] (.transp (ua e sorry sorry) .bot a) =
eval [] (.app e a) :=
sorry
```
### 5.8 Equivalences and Fibers
```lean
-- Cells/Cubical/Equiv.lean
/-- The fiber of f over b: Σ(a : A), f(a) =_B b -/
def fiberTy (A B : CType) (f : CTerm) (b : CTerm) : CType :=
.sigma A (.pathTy (weakenType B) (.app (weakenTerm f) (.var 0)) (weakenTerm b))
/-- An equivalence e : A ≃ B consists of:
- a map f : A → B
- for every b : B, a contractible fiber -/
structure EquivData where
f : CTerm -- the forward map
fInv : CTerm -- the inverse (for computation)
sec : CTerm -- section: f(fInv(b)) = b
ret : CTerm -- retraction: fInv(f(a)) = a
coh : CTerm -- coherence (half-adjoint)
/-- The identity equivalence on a type. -/
def idEquiv (A : CType) : EquivData where
f := .lam (.var 0) -- id
fInv := .lam (.var 0) -- id
sec := .lam (.dimLam (.var 1)) -- refl
ret := .lam (.dimLam (.var 1)) -- refl
coh := sorry -- trivial coherence
```
---
## 6. Cells as Transports
### 6.1 The Cell Structure
```lean
-- Cells/Cell/Basic.lean
/-- A cell is a line of types: a transport between its boundary faces.
This is the fundamental object of the system. -/
structure Cell where
/-- The line of types: (i : 𝕀) ⊢ A(i) type.
Represented as a dim-lambda returning a type term. -/
line : CTerm
/-- The input boundary: A(0). -/
input : CType
/-- The output boundary: A(1). -/
output : CType
/-- Proof that input matches the line evaluated at 0. -/
input_spec : evalType #[] (dimSubst line .i0) = evalType #[] (.ann (.var 0) input)
/-- Proof that output matches the line evaluated at 1. -/
output_spec : evalType #[] (dimSubst line .i1) = evalType #[] (.ann (.var 0) output)
/-- The transport function induced by a cell. -/
def Cell.asTransport (c : Cell) : CTerm :=
.lam (.transp c.line .bot (.var 0))
/-- The identity cell on a type: the constant line. -/
def Cell.id (A : CType) : Cell where
line := .dimLam (.ann (.var 0) A) -- A for all i
input := A
output := A
input_spec := rfl
output_spec := rfl
/-- A cell from an equivalence (via univalence). -/
def Cell.ofEquiv (e : EquivData) (A B : CType) : Cell where
line := ua e.f A B
input := A
output := B
input_spec := ua_zero e.f A B
output_spec := ua_one e.f A B
```
### 6.2 Cell Composition
```lean
-- Cells/Cell/Compose.lean
/-- Sequential composition: concatenate lines.
c₁ : A ↔ B and c₂ : B ↔ C gives c₁ ∘ c₂ : A ↔ C -/
def Cell.seq (c₁ c₂ : Cell) (h : c₁.output = c₂.input) : Cell where
line := concatDimLines c₁.line c₂.line h
input := c₁.input
output := c₂.output
input_spec := sorry
output_spec := sorry
/-- Parallel composition: product of lines.
c₁ : A₁ ↔ B₁ and c₂ : A₂ ↔ B₂ gives c₁ × c₂ : A₁×A₂ ↔ B₁×B₂ -/
def Cell.par (c₁ c₂ : Cell) : Cell where
line := .dimLam (.ann (.var 0) (.sigma
(dimSubstType c₁.line (.var 0))
(dimSubstType c₂.line (.var 0))))
input := .sigma c₁.input c₂.input
output := .sigma c₁.output c₂.output
input_spec := sorry
output_spec := sorry
/-- Inverse cell: reverse the line.
c : A ↔ B gives c⁻¹ : B ↔ A -/
def Cell.inv (c : Cell) : Cell where
line := .dimLam (dimSubst c.line (.inv (.var 0)))
input := c.output
output := c.input
input_spec := sorry
output_spec := sorry
/-- Monad structure on cells.
return = Cell.id
bind = Cell.seq
Laws hold by groupoid laws on paths. -/
theorem cell_left_unit (c : Cell) :
Cell.seq (Cell.id c.input) c sorry ≈ c :=
sorry -- follows from transp along constant line = id
theorem cell_right_unit (c : Cell) :
Cell.seq c (Cell.id c.output) sorry ≈ c :=
sorry
theorem cell_assoc (c₁ c₂ c₃ : Cell) (h₁₂ h₂₃ : _) :
Cell.seq (Cell.seq c₁ c₂ h₁₂) c₃ h₂₃ ≈
Cell.seq c₁ (Cell.seq c₂ c₃ sorry) sorry :=
sorry -- follows from path concatenation associativity
```
### 6.3 Higher Cells
```lean
-- Cells/Cell/Higher.lean
/-- A 2-cell is a homotopy between two 1-cells.
It is a square: a term (i, j : 𝕀) ⊢ A(i,j) type -/
structure Cell2 where
/-- The two 1-cells being related. -/
top : Cell -- the cell at j = 1
bottom : Cell -- the cell at j = 0
/-- Side cells connecting endpoints. -/
left : Cell -- input side: top.input ↔ bottom.input
right : Cell -- output side: top.output ↔ bottom.output
/-- The filling: a square of types. -/
square : CTerm -- (i, j : 𝕀) ⊢ A(i,j) type
/-- Boundary coherence. -/
face_top : dimSubst (dimSubst square (.var 1)) .i1 = top.line
face_bottom : dimSubst (dimSubst square (.var 1)) .i0 = bottom.line
face_left : dimSubst (dimSubst square (.var 0)) .i0 = left.line
face_right : dimSubst (dimSubst square (.var 0)) .i1 = right.line
/-- An n-cell is an n-cube of transports. -/
inductive CellN : Nat → Type where
| point : CType → CellN 0
| arrow : Cell → CellN 1
| cube : {n : Nat} →
(boundary : Fin (2 * (n + 1)) → CellN n) →
(filler : CTerm) →
CellN (n + 1)
/-- The dimension hierarchy:
0-cell = a value (a type, a constant, a color)
1-cell = a transport (a function, a shader pass, a wire)
2-cell = a homotopy (a continuous deformation, a parameter sweep)
3-cell = a homotopy of homotopies (an animation of an animation)
n-cell = full generality -/
```
### 6.4 Fibers and Projections
```lean
-- Cells/Cell/Fiber.lean
/-- A projection from an n-cell to a 2D screen manifold. -/
structure Projection (n : Nat) where
/-- The cell being projected. -/
source : CellN n
/-- The projection map: from the n-cell to ℝ². -/
proj : CTerm -- source → (Float × Float)
/-- The fiber of a projection at a screen point:
the set of computational elements that map to that pixel. -/
def Projection.fiberAt (π : Projection n) (screenPos : Float × Float) : CType :=
fiberTy sorry sorry π.proj (.pair (.floatLit screenPos.1) (.floatLit screenPos.2))
/-- Fiber selection: choosing an element of the fiber.
Mouse click = evaluating a section of the projection sheaf. -/
structure FiberSelection (n : Nat) where
proj : Projection n
pos : Float × Float
element : CTerm -- an element of fiberAt proj pos
/-- Continuous fiber deformation: dragging = transporting a section
along a path in screen space. -/
structure Drag (n : Nat) where
proj : Projection n
path : CTerm -- 𝕀 → (Float × Float), the mouse path
start : FiberSelection n
-- The section being transported:
-- for each point along the drag path,
-- a fiber element connected to the previous one by transport
section_ : CTerm -- (i : 𝕀) → fiber(path(i))
```
---
## 7. Cell Graph and Reactivity
### 7.1 The Cell Network
```lean
-- Cells/Cell/Graph.lean
/-- Identifier for a cell in a graph. -/
abbrev CellId := Nat
/-- A port is one face of a cell. -/
structure Port where
cellId : CellId
face : Fin 2 -- 0 = input, 1 = output
/-- A wire connects two ports with a proof of type compatibility. -/
structure Wire where
source : Port
target : Port
-- Type at source face must equal type at target face
compat : CTerm -- proof term (in the cubical calculus)
/-- A cell graph: the workspace. -/
structure CellGraph where
cells : Array Cell
wires : Array Wire
-- No dangling wires
wires_valid : ∀ w ∈ wires,
w.source.cellId < cells.size ∧
w.target.cellId < cells.size
/-- Get the type at a port. -/
def CellGraph.portType (g : CellGraph) (p : Port) : CType :=
let cell := g.cells[p.cellId]!
if p.face == 0 then cell.input else cell.output
/-- Flatten a cell graph to a single composite cell.
Topological sort, then compose sequentially.
Parallel branches become Σ-type transports. -/
def CellGraph.flatten (g : CellGraph) : Cell :=
sorry -- topological sort + fold with Cell.seq and Cell.par
/-- Find all cells downstream of a changed cell. -/
def CellGraph.downstream (g : CellGraph) (changed : CellId) : Array CellId :=
sorry -- BFS/DFS on the wire graph from `changed`
```
### 7.2 Incremental Re-typechecking
```lean
-- Cells/Reactive/Incremental.lean
/-- When a cell is modified, determine which wires need rechecking. -/
def recheckNeeded (g : CellGraph) (modified : CellId) : Array Nat :=
g.wires.zipWithIndex.filterMap fun ⟨w, i⟩ =>
if w.source.cellId == modified || w.target.cellId == modified
then some i
else none
/-- Result of incremental typechecking. -/
inductive RecheckResult where
| ok : CellGraph → RecheckResult -- all wires still valid
| broken : Array Nat → RecheckResult -- these wire indices failed
/-- Incrementally recheck after modification. -/
def incrementalRecheck (g : CellGraph) (modified : CellId)
(newCell : Cell) : RecheckResult :=
let g' := { g with cells := g.cells.set! modified newCell }
let toCheck := recheckNeeded g' modified
let broken := toCheck.filter fun i =>
let w := g'.wires[i]!
g'.portType w.source != g'.portType w.target
if broken.isEmpty then .ok g' else .broken broken
```
---
## 8. Color as a Cell
### 8.1 Color Spaces as Types
```lean
-- Cells/Color/Space.lean
/-- A color space is a type in the cell calculus. -/
inductive ColorSpace : Type where
| sRGB : ColorSpace -- standard RGB (gamma-encoded)
| linearRGB : ColorSpace -- linear light RGB
| oklch : ColorSpace -- perceptual: lightness, chroma, hue
| oklab : ColorSpace -- perceptual: lightness, a*, b*
| hsl : ColorSpace -- hue, saturation, lightness
| xyz : ColorSpace -- CIE XYZ
deriving Repr, BEq
/-- Each color space has a representation as a triple of floats. -/
def ColorSpace.toCType : ColorSpace → CType
| _ => .sigma (.el (.ann (.floatLit 0) sorry))
(.sigma (.el (.ann (.floatLit 0) sorry))
(.el (.ann (.floatLit 0) sorry)))
/-- A color is a cell with a specific color space type. -/
structure Color where
space : ColorSpace
value : CTerm -- a triple of floats in that space
/-- A color transform is a cell between color spaces:
the transport IS the color conversion. -/
def colorConvert (from to : ColorSpace) : Cell :=
Cell.ofEquiv (colorConvertEquiv from to) from.toCType to.toCType
/-- sRGB ↔ linear RGB conversion, as an equivalence. -/
def srgbLinearEquiv : EquivData where
f := sorry -- gamma decode: c < 0.04045 ? c/12.92 : ((c+0.055)/1.055)^2.4
fInv := sorry -- gamma encode: c < 0.0031308 ? 12.92*c : 1.055*c^(1/2.4)-0.055
sec := sorry
ret := sorry
coh := sorry
```
### 8.2 Gamut Mapping as Transport
```lean
-- Cells/Color/Transport.lean
/-- Gamut mapping: projecting an out-of-gamut color into the displayable set.
This is a SECTION of the inclusion Display ↪ Perceptual.
Not an equivalence — it's a retraction, hence lossy.
But it IS a cell: it transports along a line in color space
that moves from the intended color to the nearest displayable color. -/
def gamutMap (display : ColorSpace) (color : Color) : Cell :=
sorry -- line from color.value to nearest in-gamut point
```
---
## 9. Shader Compilation
### 9.1 Shader IR
```lean
-- Cells/Shader/IR.lean
/-- Types available in shader code. -/
inductive ShaderType : Type where
| float : ShaderType
| vec2 : ShaderType
| vec3 : ShaderType
| vec4 : ShaderType
| mat2 : ShaderType
| mat3 : ShaderType
| mat4 : ShaderType
| int : ShaderType
| bool : ShaderType
| sampler : ShaderType -- sampler2D
deriving Repr, BEq
/-- Shader expressions (no side effects, no control flow). -/
inductive ShaderExpr : Type where
| litF : Float → ShaderExpr
| litI : Int → ShaderExpr
| litB : Bool → ShaderExpr
| var : String → ShaderExpr
| binop : String → ShaderExpr → ShaderExpr → ShaderExpr
| unop : String → ShaderExpr → ShaderExpr
| call : String → List ShaderExpr → ShaderExpr
| swizzle : ShaderExpr → String → ShaderExpr
| ternary : ShaderExpr → ShaderExpr → ShaderExpr → ShaderExpr
| vecCon : List ShaderExpr → ShaderExpr -- vec3(x,y,z)
| matCon : List ShaderExpr → ShaderExpr -- mat3(...)
| texSample : ShaderExpr → ShaderExpr → ShaderExpr -- texture(s, uv)
deriving Repr
/-- Shader statements (sequential, with control flow). -/
inductive ShaderStmt : Type where
| decl : ShaderType → String → ShaderExpr → ShaderStmt
| assign : String → ShaderExpr → ShaderStmt
| forLoop : String → ShaderExpr → ShaderExpr → List ShaderStmt → ShaderStmt
| ifThen : ShaderExpr → List ShaderStmt → List ShaderStmt → ShaderStmt
| ret : ShaderExpr → ShaderStmt
| discard : ShaderStmt
deriving Repr
/-- A complete shader program. -/
structure ShaderProgram where
version : String := "450"
uniforms : List (ShaderType × String)
inputs : List (ShaderType × String) -- varyings in
outputs : List (ShaderType × String) -- varyings out / fragColor
body : List ShaderStmt
deriving Repr
```
### 9.2 Verified Compilation
```lean
-- Cells/Shader/Compile.lean
/-- Judgment: a cell term is shader-compilable.
This restricts to the fragment of the cubical calculus
that maps onto GPU operations. -/
inductive Compilable : CTerm → Prop where
| float_lit : ∀ f, Compilable (.floatLit f)
| vec_lit : ∀ v, Compilable (.vecLit v)
| var : ∀ n, Compilable (.var n)
| app : Compilable f → Compilable a → Compilable (.app f a)
| lam : Compilable body → Compilable (.lam body)
| pair : Compilable a → Compilable b → Compilable (.pair a b)
| fst : Compilable t → Compilable (.fst t)
| snd : Compilable t → Compilable (.snd t)
-- No transp, hcomp, glue, dimLam — these must be
-- reduced away before compilation
/-- Compile a compilable cell term to shader IR.
The key invariant: the compiled shader computes the
same function as the cell's transport, at the level
of floating-point semantics. -/
def compileCell (t : CTerm) (h : Compilable t)
(ctx : CompileContext) : ShaderProgram :=
sorry -- structural recursion on the compilability proof
/-- Attempt to make a term compilable by reducing away
all cubical operations (transp, hcomp, Glue) to
their concrete computational content. -/
def reduceToConcrete (t : CTerm) : Option CTerm :=
sorry -- run the evaluator, read back, check Compilable
/-- The full pipeline:
Cell → reduce cubical ops → compile to ShaderIR → emit GLSL -/
def cellToGLSL (c : Cell) : Option String := do
let reduced ← reduceToConcrete c.asTransport
let h : Compilable reduced := sorry -- check
let program := compileCell reduced h {}
pure (emitGLSL program)
```
### 9.3 GLSL Emission (Lean side)
```lean
-- Cells/Shader/Emit.lean
/-- Emit a shader expression as a GLSL string. -/
def emitExpr : ShaderExpr → String
| .litF f => toString f
| .litI i => toString i
| .litB true => "true"
| .litB false => "false"
| .var name => name
| .binop op l r => s!"({emitExpr l} {op} {emitExpr r})"
| .unop op e => s!"({op}{emitExpr e})"
| .call fn args =>
s!"{fn}({", ".intercalate (args.map emitExpr)})"
| .swizzle e c => s!"{emitExpr e}.{c}"
| .ternary c t f =>
s!"({emitExpr c} ? {emitExpr t} : {emitExpr f})"
| .vecCon cs =>
s!"vec{cs.length}({", ".intercalate (cs.map emitExpr)})"
| .matCon cs =>
s!"mat{cs.length}({", ".intercalate (cs.map emitExpr)})"
| .texSample s uv =>
s!"texture({emitExpr s}, {emitExpr uv})"
/-- Emit a shader type as GLSL. -/
def emitType : ShaderType → String
| .float => "float"
| .vec2 => "vec2"
| .vec3 => "vec3"
| .vec4 => "vec4"
| .mat2 => "mat2"
| .mat3 => "mat3"
| .mat4 => "mat4"
| .int => "int"
| .bool => "bool"
| .sampler => "sampler2D"
/-- Emit a complete shader program as GLSL source. -/
def emitGLSL (prog : ShaderProgram) : String :=
let header := s!"#version {prog.version}\n\n"
let uniforms := prog.uniforms.map fun ⟨ty, name⟩ =>
s!"uniform {emitType ty} {name};\n"
let inputs := prog.inputs.map fun ⟨ty, name⟩ =>
s!"in {emitType ty} {name};\n"
let outputs := prog.outputs.map fun ⟨ty, name⟩ =>
s!"out {emitType ty} {name};\n"
let body := emitStmts prog.body 1
header ++
String.join uniforms ++ "\n" ++
String.join inputs ++
String.join outputs ++ "\n" ++
"void main() {\n" ++ body ++ "}\n"
where
emitStmts (stmts : List ShaderStmt) (indent : Nat) : String :=
String.join (stmts.map (emitStmt · indent))
emitStmt (stmt : ShaderStmt) (indent : Nat) : String :=
let pad := String.mk (List.replicate (indent * 4) ' ')
match stmt with
| .decl ty name expr =>
s!"{pad}{emitType ty} {name} = {emitExpr expr};\n"
| .assign name expr =>
s!"{pad}{name} = {emitExpr expr};\n"
| .forLoop var lo hi body =>
s!"{pad}for (int {var} = {emitExpr lo}; {var} < {emitExpr hi}; {var}++) \{\n" ++
emitStmts body (indent + 1) ++
s!"{pad}}\n"
| .ifThen cond then_ else_ =>
s!"{pad}if ({emitExpr cond}) \{\n" ++
emitStmts then_ (indent + 1) ++
s!"{pad}} else \{\n" ++
emitStmts else_ (indent + 1) ++
s!"{pad}}\n"
| .ret expr =>
s!"{pad}fragColor = {emitExpr expr};\n"
| .discard =>
s!"{pad}discard;\n"
```
---
## 10. Runtime — FFI Boundary
### 10.1 Opaque GPU Types
```lean
-- Cells/Runtime/GPU.lean
/-- Opaque pointer to the Rust-backed GPU context.
Invariant: a valid OpenGL/Vulkan context is active. -/
opaque GPUContextPointer : NonemptyType
def GPUContext : Type := GPUContextPointer.type
instance : Nonempty GPUContext := GPUContextPointer.property
/-- Opaque handle to a compiled shader program on GPU. -/
opaque ShaderHandlePointer : NonemptyType
def ShaderHandle : Type := ShaderHandlePointer.type
instance : Nonempty ShaderHandle := ShaderHandlePointer.property
/-- Create a GPU context with a window. -/
@[extern "cells_gpu_create"]
opaque GPU.create (width height : UInt32) (title : @& String) : IO GPUContext
/-- Destroy a GPU context. -/
@[extern "cells_gpu_destroy"]
opaque GPU.destroy (ctx : @& GPUContext) : IO Unit
/-- Compile a GLSL source string to a GPU shader program. -/
@[extern "cells_gpu_compile_shader"]
opaque GPU.compileShader (ctx : @& GPUContext) (glsl : @& String) : IO ShaderHandle
/-- Set uniform values on a shader program. -/
@[extern "cells_gpu_set_uniforms"]
opaque GPU.setUniforms (ctx : @& GPUContext) (handle : @& ShaderHandle)
(uniforms : @& FloatArray) : IO Unit
/-- Render a fullscreen quad with the given shader. -/
@[extern "cells_gpu_render_quad"]
opaque GPU.renderQuad (ctx : @& GPUContext) (handle : @& ShaderHandle) : IO Unit
/-- Swap buffers (present the rendered frame). -/
@[extern "cells_gpu_swap"]
opaque GPU.swap (ctx : @& GPUContext) : IO Unit
/-- Check if the window should close. -/
@[extern "cells_gpu_should_close"]
opaque GPU.shouldClose (ctx : @& GPUContext) : IO Bool
```
### 10.2 Input Events
```lean
-- Cells/Runtime/Input.lean
/-- Raw input events from the windowing system. -/
inductive InputEvent : Type where
| mouseMove : Float → Float → InputEvent -- x, y in [0,1]²
| mouseDown : Float → Float → Nat → InputEvent -- x, y, button
| mouseUp : Float → Float → Nat → InputEvent
| mouseDrag : Float → Float → Float → Float → InputEvent -- x, y, dx, dy
| keyDown : UInt32 → InputEvent
| keyUp : UInt32 → InputEvent
| scroll : Float → Float → InputEvent -- dx, dy
| resize : UInt32 → UInt32 → InputEvent -- width, height
| quit : InputEvent
| none : InputEvent
deriving Repr
/-- Poll for the next input event. -/
@[extern "cells_gpu_poll_input"]
opaque GPU.pollInput (ctx : @& GPUContext) : IO InputEvent
```
### 10.3 The Interaction Loop
```lean
-- Cells/Runtime/Loop.lean
/-- The workspace state: a cell graph + current projection + selection. -/
structure WorkspaceState where
graph : CellGraph
projection : Projection 1 -- current view
selection : Option (FiberSelection 1)
uniforms : FloatArray -- current parameter values
/-- The main interaction loop.
This is a 3-cell in disguise: the session is a path
through workspace states, and the rendering at each
moment is the projection of the current state. -/
partial def mainLoop (ctx : GPUContext) (handle : ShaderHandle)
(state : WorkspaceState) : IO Unit := do
-- Check exit
if ← GPU.shouldClose ctx then return
-- Poll input
let event ← GPU.pollInput ctx
-- Process event
let state' ← match event with
| .mouseDown x y _ =>
-- Fiber selection: find which cell projects to (x, y)
let fiber := selectFiber state.graph state.projection (x, y)
pure { state with selection := fiber }
| .mouseDrag x y dx dy =>
match state.selection with
| some sel =>
-- Transport the selection along the drag direction
let newParams := applyDeformation state.uniforms sel dx dy
GPU.setUniforms ctx handle newParams
pure { state with uniforms := newParams }
| none => pure state
| .mouseUp _ _ _ =>
pure { state with selection := none }
| .keyDown key =>
-- Keyboard: cell manipulation commands
handleKeyCommand state key
| .resize w h =>
-- Recompute projection for new window size
pure state -- TODO
| _ => pure state
-- Render
GPU.renderQuad ctx handle
GPU.swap ctx
-- Continue
mainLoop ctx handle state'
/-- Entry point. -/
def main (args : List String) : IO Unit := do
-- Create context
let ctx ← GPU.create 1280 720 "Cells"
-- Build initial cell graph (a trivial example)
let graph := initialGraph
let shader ← compileGraph graph
let handle ← GPU.compileShader ctx shader
-- Initial state
let state : WorkspaceState := {
graph := graph
projection := defaultProjection
selection := none
uniforms := FloatArray.mk #[0.0, 0.0, 1.0] -- time, mouse_x, mouse_y
}
-- Run
mainLoop ctx handle state
GPU.destroy ctx
```
---
## 11. Rust Native Backend Specification
> **Note on code samples below.** The C++ code examples retained from an
> earlier draft of this spec are illustrative of the *Lean FFI pattern*
> (taking `b_lean_obj_arg`, returning `lean_obj_res`, the
> `register_class` + finalizer dance). Rust's `extern "C"` functions
> mirror these shapes 1:1 via the `lean-sys` crate (bindgen-generated
> bindings to `lean/lean.h`), so the interop story is structurally
> identical. When the actual Rust crate is written, these C++ snippets
> become Rust equivalents without changes to the FFI boundary or the
> Lean-side `@[extern]` declarations.
### 11.1 GPU Context (context.cpp)
```cpp
// native/src/gpu/context.cpp
#include <lean/lean.h>
#include <GLFW/glfw3.h>
// Include glad or similar for GL function loading
struct CellsGPUContext {
GLFWwindow* window;
uint32_t width;
uint32_t height;
GLuint fullscreenVAO; // VAO for fullscreen quad
GLuint fullscreenVBO;
};
// Destructor for Lean's external object system
static void cells_gpu_finalize(void* ptr) {
auto* ctx = static_cast<CellsGPUContext*>(ptr);
if (ctx->window) {
glfwDestroyWindow(ctx->window);
}
delete ctx;
}
// For Lean GC traversal (no Lean objects inside)
static void cells_gpu_foreach(void* ptr, b_lean_obj_arg fn) {
// No Lean objects to traverse
}
extern "C" LEAN_EXPORT lean_obj_res cells_gpu_create(
uint32_t width, uint32_t height,
b_lean_obj_arg title, lean_obj_arg world
) {
if (!glfwInit()) {
return lean_io_result_mk_error(
lean_mk_io_user_error(
lean_mk_string("Failed to initialize GLFW")));
}
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 4);
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 5);
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
const char* title_str = lean_string_cstr(title);
GLFWwindow* win = glfwCreateWindow(
width, height, title_str, nullptr, nullptr);
if (!win) {
glfwTerminate();
return lean_io_result_mk_error(
lean_mk_io_user_error(
lean_mk_string("Failed to create window")));
}
glfwMakeContextCurrent(win);
// Load GL functions (glad, GLEW, etc.)
// gladLoadGLLoader((GLADloadproc)glfwGetProcAddress);
// Create fullscreen quad VAO
auto* ctx = new CellsGPUContext{win, width, height, 0, 0};
setup_fullscreen_quad(ctx);
lean_object* obj = lean_alloc_external(
cells_gpu_finalize, cells_gpu_foreach, ctx);
return lean_io_result_mk_ok(obj);
}
extern "C" LEAN_EXPORT lean_obj_res cells_gpu_destroy(
b_lean_obj_arg ctx_obj, lean_obj_arg world
) {
// Destructor will be called by Lean GC
// But we can force cleanup here
auto* ctx = static_cast<CellsGPUContext*>(
lean_get_external_data(ctx_obj));
if (ctx->window) {
glfwDestroyWindow(ctx->window);
ctx->window = nullptr;
}
glfwTerminate();
return lean_io_result_mk_ok(lean_box(0));
}
```
### 11.2 Shader Compilation (shader.cpp)
```cpp
// native/src/gpu/shader.cpp
#include <lean/lean.h>
// Minimal vertex shader for fullscreen quad
static const char* FULLSCREEN_VERT = R"(
#version 450
out vec2 fragCoord;
void main() {
vec2 pos = vec2(gl_VertexID & 1, (gl_VertexID >> 1) & 1) * 2.0 - 1.0;
fragCoord = pos * 0.5 + 0.5;
gl_Position = vec4(pos, 0.0, 1.0);
}
)";
extern "C" LEAN_EXPORT lean_obj_res cells_gpu_compile_shader(
b_lean_obj_arg ctx_obj,
b_lean_obj_arg glsl_src,
lean_obj_arg world
) {
const char* frag_src = lean_string_cstr(glsl_src);
// Compile vertex shader
GLuint vert = glCreateShader(GL_VERTEX_SHADER);
glShaderSource(vert, 1, &FULLSCREEN_VERT, nullptr);
glCompileShader(vert);
// ... check errors ...
// Compile fragment shader
GLuint frag = glCreateShader(GL_FRAGMENT_SHADER);
glShaderSource(frag, 1, &frag_src, nullptr);
glCompileShader(frag);
// ... check errors ...
// Link program
GLuint program = glCreateProgram();
glAttachShader(program, vert);
glAttachShader(program, frag);
glLinkProgram(program);
// ... check errors ...
glDeleteShader(vert);
glDeleteShader(frag);
// Return program handle as external object
// (or boxed uint if handles fit in lean_box)
return lean_io_result_mk_ok(lean_box(program));
}
extern "C" LEAN_EXPORT lean_obj_res cells_gpu_set_uniforms(
b_lean_obj_arg ctx_obj,
b_lean_obj_arg handle_obj,
b_lean_obj_arg uniforms,
lean_obj_arg world
) {
uint32_t program = lean_unbox(handle_obj);
glUseProgram(program);
// Walk the FloatArray and set uniforms by index
size_t n = lean_sarray_size(uniforms);
double* data = lean_float_array_cptr(uniforms);
for (size_t i = 0; i < n; i++) {
// Convention: uniform locations are sequential
glUniform1f(i, (float)data[i]);
}
return lean_io_result_mk_ok(lean_box(0));
}
extern "C" LEAN_EXPORT lean_obj_res cells_gpu_render_quad(
b_lean_obj_arg ctx_obj,
b_lean_obj_arg handle_obj,
lean_obj_arg world
) {
auto* ctx = static_cast<CellsGPUContext*>(
lean_get_external_data(ctx_obj));
uint32_t program = lean_unbox(handle_obj);
glClear(GL_COLOR_BUFFER_BIT);
glUseProgram(program);
glBindVertexArray(ctx->fullscreenVAO);
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
return lean_io_result_mk_ok(lean_box(0));
}
```
### 11.3 Input Handling (input.cpp)
```cpp
// native/src/gpu/input.cpp
#include <lean/lean.h>
#include <GLFW/glfw3.h>
// InputEvent constructors match the Lean inductive:
// 0 = mouseMove, 1 = mouseDown, 2 = mouseUp,
// 3 = mouseDrag, 4 = keyDown, 5 = keyUp,
// 6 = scroll, 7 = resize, 8 = quit, 9 = none
extern "C" LEAN_EXPORT lean_obj_res cells_gpu_poll_input(
b_lean_obj_arg ctx_obj, lean_obj_arg world
) {
auto* ctx = static_cast<CellsGPUContext*>(
lean_get_external_data(ctx_obj));
glfwPollEvents();
if (glfwWindowShouldClose(ctx->window)) {
// Return InputEvent.quit (tag 8, no fields)
lean_object* ev = lean_alloc_ctor(8, 0, 0);
return lean_io_result_mk_ok(ev);
}
// Check mouse position
double mx, my;
glfwGetCursorPos(ctx->window, &mx, &my);
double nx = mx / ctx->width;
double ny = my / ctx->height;
// Return InputEvent.mouseMove (tag 0, 2 float fields)
lean_object* ev = lean_alloc_ctor(0, 0, 2 * sizeof(double));
lean_ctor_set_float(ev, 0, nx);
lean_ctor_set_float(ev, sizeof(double), ny);
return lean_io_result_mk_ok(ev);
}
```
---
## 12. Build Configuration
### 12.1 lakefile.lean
```lean
import Lake
open Lake DSL System
def nativeDir : FilePath := "native"
def gpuLinkArgs : Array String :=
if System.Platform.isOSX then
#["-framework", "OpenGL", "-lglfw",
"-L/opt/homebrew/lib", "-I/opt/homebrew/include"]
else if System.Platform.isWindows then
#["-lopengl32", "-lglfw3"]
else -- Linux
#["-lGL", "-lglfw", "-lm", "-ldl", "-lpthread"]
package cells where
moreLinkArgs := gpuLinkArgs
@[default_target]
lean_lib Cells where
roots := #[`Cells]
lean_exe cellsApp where
root := `Main
moreLinkArgs := gpuLinkArgs
```
### 12.2 CMakeLists.txt (native/)
```cmake
cmake_minimum_required(VERSION 3.16)
project(cells_native C CXX)
set(CMAKE_CXX_STANDARD 17)
find_package(OpenGL REQUIRED)
find_package(glfw3 3.3 REQUIRED)
add_library(cells_native STATIC
src/gpu/context.cpp
src/gpu/shader.cpp
src/gpu/framebuffer.cpp
src/gpu/input.cpp
src/emit/glsl.cpp
src/emit/wgsl.cpp
src/numeric/blas.cpp
src/numeric/mesh.cpp
src/numeric/solver.cpp
)
target_include_directories(cells_native PUBLIC
include/
${LEAN_INCLUDE_DIR}
)
target_link_libraries(cells_native
OpenGL::GL
glfw
)
```
---
## 13. Development Roadmap
### Phase 1: Cubical Core (weeks 16)
| Week | Deliverable | Test |
|------|-------------|------|
| 1 | `Interval.lean`, `Face.lean`, `Syntax.lean` | de Morgan laws, substitution |
| 2 | `Value.lean`, `Eval.lean` (λ-calculus fragment) | eval roundtrip for simple terms |
| 3 | `Transport.lean` (Π, Σ cases) | transport along refl = id |
| 4 | `Transport.lean` (Path case), `Comp.lean` | composition fills boxes |
| 5 | `Glue.lean`, `Equiv.lean` | ua computes at endpoints |
| 6 | `Soundness.lean` | transp_ua, monad laws |
### Phase 2: Cells (weeks 79)
| Week | Deliverable | Test |
|------|-------------|------|
| 7 | `Cell/Basic.lean`, `Compose.lean` | id is neutral, seq is assoc |
| 8 | `Cell/Higher.lean`, `Fiber.lean` | 2-cells compose, fibers compute |
| 9 | `Cell/Graph.lean`, `Reactive/` | graph flatten matches manual comp |
**Phase 2 higher-cell backend — Zigzag Lean Port.** `Cell/Higher.lean`
depends on the n-category combinatorial engine: dimension-general
normalisation that correctly preserves essential identities past
dimension 4 (where homotopy.io historically caps out). This engine
is **ported into Lean** from the Rust reference at
`zigzag-engine/` — not consumed as an FFI dependency. See
`ZIGZAG_PORT.md` for the 10-step port plan. The port adds
`Topolei/Zigzag/{Monotone, Core, Diagram, Signature, Degeneracy,
Pullback, Normalise, Typecheck, Tests}.lean` + `Cell/Zigzag.lean`
bridge. Deliverables are Lean-native, pure functional, kernel-checked.
Independent of the Rust evaluator FFI; can proceed in parallel with it.
### Phase 3: Shader Pipeline (weeks 1013)
| Week | Deliverable | Test |
|------|-------------|------|
| 10 | `Shader/IR.lean`, `Emit.lean` | emit a hardcoded ShaderProgram → valid GLSL |
| 11 | `Shader/Compile.lean` | compile a float-arithmetic cell to ShaderIR |
| 12 | `native/gpu/` + FFI wrappers | open window, render solid color |
| 13 | End-to-end: cell → shader → GPU | render a gradient from a cell definition |
### Phase 4: Interaction (weeks 1417)
| Week | Deliverable | Test |
|------|-------------|------|
| 14 | `Runtime/Input.lean`, `Loop.lean` | mouse events reach Lean |
| 15 | Fiber selection + drag deformation | drag changes uniform, visual updates |
| 16 | `Color/Space.lean`, color cells | color space transport renders correctly |
| 17 | `examples/Interactive.lean` | mouse-driven procedural shader |
### Phase 5: Boundary and Security (weeks 1822)
| Week | Deliverable | Test |
|------|-------------|------|
| 18 | `Boundary/Access.lean`, `ProCell.lean` | modal cells annotate correctly |
| 19 | `Boundary/Guard.lean`, `Security.lean` | guard cells block invalid crossings |
| 20 | `Boundary/Hardware.lean`, `Kernel.lean` | hardware model compiles, sealed tower types check |
| 21 | `Boundary/Classifier.lean`, `Expansion.lean` | boundary transport moves access levels |
| 22 | `SecureCellGraph`, wgpu `ToolIntegration` | cross-boundary wires require guards, wgpu pullback works |
### Phase 6: Self-Hosting (weeks 23+)
| Milestone | Deliverable |
|-----------|-------------|
| 2325 | `Meta/DSL.lean`: syntax extensions for cell notation |
| 2629 | `Meta/Widget.lean`: cell graph editor as a cell graph |
| 30+ | The editor edits itself. The system is complete. |
---
## 14. Key Theorems (Proof Obligations)
These are the theorems that must be proved (or deliberately `sorry`'d with documented intent) for the system to be sound.
### Cubical Core
| Theorem | Statement | Priority |
|---------|-----------|----------|
| `transp_refl` | Transport along a constant line is the identity | Critical |
| `transp_concat` | Transport along a concatenation is composition of transports | Critical |
| `hcomp_faces` | Composition agrees with the tube on constrained faces | Critical |
| `ua_endpoints` | `ua(e)` evaluates to `A` at 0 and `B` at 1 | Critical |
| `transp_ua` | Transport along `ua(e)` computes as `e` | Critical |
| `glue_beta` | `unglue (glue t a) = a` | High |
| `glue_eta` | `glue (unglue g) = g` on appropriate faces | High |
### Cell Layer
| Theorem | Statement | Priority |
|---------|-----------|----------|
| `cell_left_unit` | `id ∘ c ≈ c` | High |
| `cell_right_unit` | `c ∘ id ≈ c` | High |
| `cell_assoc` | `(c₁ ∘ c₂) ∘ c₃ ≈ c₁ ∘ (c₂ ∘ c₃)` | High |
| `cell_inv_left` | `c⁻¹ ∘ c ≈ id` | Medium |
| `par_seq_interchange` | `(c₁ × c₂) ∘ (c₃ × c₄) ≈ (c₁ ∘ c₃) × (c₂ ∘ c₄)` | Medium |
### Shader Compilation
| Theorem | Statement | Priority |
|---------|-----------|----------|
| `compile_sound` | Compiled shader computes same function as cell transport | Critical |
| `reduce_complete` | If a cell has no free dim variables, `reduceToConcrete` succeeds | High |
| `emit_parse_roundtrip` | Emitted GLSL, if parsed, gives back the same ShaderIR | Medium |
### Boundary and Security
| Theorem | Statement | Priority |
|---------|-----------|----------|
| `guard_completeness` | Every cross-boundary wire has a guard cell | Critical |
| `deny_is_empty` | A `deny` guard allows no data through | Critical |
| `process_isolation` | Disjoint address spaces cannot observe each other | Critical |
| `isa_sheaf_condition` | ISA-level observations are invariant under microarch change | High |
| `boundary_transport` | Tool absorption moves access levels monotonically upward | High |
| `pullback_agreement` | Pullback cells agree with external tool on shared semantics | High |
| `kan_extension_faithful` | Kan-extended cell types faithfully represent native capabilities | Medium |
| `modal_composition` | Composing modal cells preserves the minimum access level | Medium |
---
## 15. Computational Boundaries and the Accessibility Topology
This section formalizes the mathematical object that describes the border between what the cell system can access and what it cannot — and how to represent unknown, inaccessible, or adversarial computation as cells before the system has absorbed it.
### 15.1 The Problem of Separated Computational Spaces
The cell system inhabits a small subspace `C₀` of the full computational universe `U`. Outside `C₀` lie GPU compute APIs, audio engines, physics solvers, network protocols, other processes, other machines, other architectures. The boundary between `C₀` and `U \ C₀` is not a sharp wall — it has graded structure. Some external capabilities are one pullback away (wgpu), some are many abstractions away (quantum hardware), and some are adversarial (untrusted network inputs).
Below the cell system lie layers that are sealed by design: the language runtime, the kernel, the ISA, the microarchitecture, the physics of transistors. These are cells whose interiors cannot be inspected or deformed from above, but whose boundaries (the ABI, the syscall interface, the instruction encoding) are the surfaces the cell system touches.
The mathematical object that encodes all of this — the border, the grading, the accessibility structure, and the security model — is a **stratified Lawvere-Tierney topology on the subobject classifier of the cell topos**.
### 15.2 The Presheaf of Potential Cells
Before considering accessibility, define the presheaf of all potential cells. The base category `Ctx` has as objects the computational contexts in which cells can execute — "local CPU," "GPU via wgpu," "remote server," "sandboxed WASM runtime," etc. A presheaf on `Ctx` assigns to each context `c` the type of cells that *could* exist there:
```
P : Ctx^op → Type
```
The restriction maps `P(c) → P(c')` for an inclusion `c' ↪ c` express: if a cell works in a richer context, which cells still work in a poorer one.
Not every potential cell is actually available. A GPU compute cell might be well-typed in the cubical calculus but lack an FFI binding. A cell from an external library might exist but be unverified. The gap between "potential" and "actual" is the accessibility structure.
### 15.3 The Accessibility Modality
A **Lawvere-Tierney topology** `j` on the topos of presheaves over `Ctx` is an endomorphism on the subobject classifier:
```
j : Ω → Ω
```
satisfying idempotence (`j ∘ j = j`), preservation of truth (`j() = `), and preservation of conjunction (`j(p ∧ q) = j(p) ∧ j(q)`).
In the cell system, `Ω` classifies propositions about cells — "this cell exists," "this cell is well-typed," "this cell's transport is verified," "this cell's compiled shader is correct." The topology `j` selects which of these propositions count as actually accessible from the system's current vantage point.
A proposition `φ` is **`j`-true** when `j(φ) = `. The `j`-sheaves are the presheaves whose sections are determined by their `j`-local data — these are the cells the system can fully see and manipulate.
The non-sheaves are the cells at the boundary. The **sheafification** functor `L : PSh(Ctx) → Sh_j(Ctx)` is the process of absorbing external capability — pulling it into the cell system. The cells you have are the sheaves. The cells you could have are the presheaves. The boundary is the gap, and sheafification is how you close it.
### 15.4 The Accessibility Lattice
Different levels of access form a lattice of topologies, each coarser (more permissive) than the last:
```
j_verified ≤ j_typed ≤ j_compiled ≤ j_accessible ≤ j_exists ≤ j_unknown
```
In Lean 4:
```lean
-- Cells/Boundary/Access.lean
/-- Levels of accessibility for cells.
Each level corresponds to a Lawvere-Tierney topology
on the cell topos. -/
inductive AccessLevel : Type where
| verified : AccessLevel -- transport proof completed
| typed : AccessLevel -- well-typed in cubical calculus
| compiled : AccessLevel -- shader compiles, proof incomplete
| accessible : AccessLevel -- FFI binding exists, not compiled
| exists : AccessLevel -- capability known to exist externally
| unknown : AccessLevel -- beyond the border
deriving Repr, BEq, Ord
/-- A modal cell: a cell annotated with its accessibility level. -/
structure ModalCell where
cell : Cell
level : AccessLevel
evidence : CellEvidence cell level
/-- Evidence appropriate to each access level. -/
inductive CellEvidence : Cell → AccessLevel → Type where
| verified : TransportProof c → CellEvidence c .verified
| typed : TypeCheckResult c → CellEvidence c .typed
| compiled : ShaderProgram → CellEvidence c .compiled
| accessible : FFIBinding → CellEvidence c .accessible
| exists : CellSpec → CellEvidence c .exists
| unknown : CellEvidence c .unknown
```
### 15.5 Trust Boundaries and Guard Cells
Cells that cross modality boundaries have their wires annotated with the trust gap. A **guard cell** sits on a trust boundary and validates data crossing it. The guard IS a transport — it either passes data through (if valid) or blocks it:
```lean
-- Cells/Boundary/Guard.lean
/-- A wire across a trust boundary. -/
structure BoundaryWire extends Wire where
trustedSide : Port
trustedLevel : AccessLevel
untrustedSide : Port
untrustedLevel : AccessLevel
gap : trustedLevel > untrustedLevel
guard : GuardCell trustedLevel untrustedLevel
/-- The type of validation required at a trust boundary. -/
inductive GuardType where
| none : GuardType -- within trust zone, no guard needed
| typecheck : GuardType -- dynamic type check at boundary
| sanitize : GuardType -- input sanitization / normalization
| sandbox : GuardType -- execute in isolated context
| verify : GuardType -- full formal verification required
| deny : GuardType -- connection forbidden
/-- A security policy: a choice of which trust gaps are
permitted and what guards they require. -/
structure SecurityPolicy where
maxGap : Nat
guardFor : (gap : Nat) → (gap ≤ maxGap) → GuardType
trustTyped : Bool -- can verified cells receive from typed-only?
trustFFI : Bool -- can compiled cells receive from FFI-only?
trustExtern : Bool -- can any cell receive from 'exists' level?
/-- A guard cell: its transport IS the validation function. -/
def mkGuardCell (policy : SecurityPolicy) (gap : Nat)
(h : gap ≤ policy.maxGap) : Cell :=
match policy.guardFor gap h with
| .typecheck => {
line := .dimLam (.pi (.base "UntrustedInput") (.base "Result"))
input := .base "UntrustedInput"
output := .base "ValidatedInput"
input_spec := sorry
output_spec := sorry
}
| .sandbox => sorry -- transport executes in isolated context
| .deny => sorry -- transport is the empty function (⊥ → A)
| _ => sorry
```
### 15.6 Pro-Cells: Modeling the Unknown Exterior
For capabilities beyond the border — external systems you don't yet have access to and may not fully understand — the right object is a **pro-cell**: a formal cofiltered limit of increasingly refined approximations.
You can't see quantum computing cells, but you can describe increasingly refined guesses about what they would look like. Each approximation is a `CellSpec` at level `exists` or `unknown`. The actual external system is the limit of the tower — which you never reach, but you can work with any finite stage.
```lean
-- Cells/Boundary/ProCell.lean
/-- A partial description of a cell whose interior is unknown. -/
structure CellSpec where
inputHint : Option CType -- what we know about input
outputHint : Option CType -- what we know about output
constraints : List CellConstraint -- behavioral constraints
source : SpecSource -- where this spec came from
/-- Provenance of a cell specification. -/
inductive SpecSource where
| documentation : String → SpecSource
| observed : List (CTerm × CTerm) → SpecSource -- I/O pairs
| inferred : CTerm → SpecSource -- from related cells
| adversarial : SpecSource -- assume worst case
/-- A pro-cell: a cell represented by a tower of
increasingly refined approximations. -/
structure ProCell where
approx : Nat → CellSpec
refine : ∀ n, Refinement (approx (n+1)) (approx n)
level : AccessLevel
level_bound : level ≤ .exists
/-- Refinement: a later approximation is more specific. -/
structure Refinement (fine coarse : CellSpec) where
input_refines : fine.inputHint.isSome → coarse.inputHint.isSome
output_refines : fine.outputHint.isSome → coarse.outputHint.isSome
constraints_extend : coarse.constraints ⊆ fine.constraints
```
### 15.7 The Boundary Classifier as a Cell
The boundary between accessible and inaccessible computation is not a wall — it's a **classifier**. In topos theory, the subobject classifier `Ω` is an object in the topos. Since the computational universe is (modeled as) a topos, the classifier of accessibility is itself a cell.
There is a cell `∂ : CType` whose elements are propositions about accessibility. A section of `∂` over the workspace assigns to each position in the cell graph a truth value saying "is this accessible?" The boundary is the frontier of this section — the set of positions where accessibility changes.
```lean
-- Cells/Boundary/Classifier.lean
/-- The accessibility classifier as a cell type. -/
def AccessClassifier : CType :=
.pi (.base "CellId") (.base "AccessLevel")
/-- A boundary is a cell whose type is the accessibility
classifier. Its transport moves the boundary: as you
absorb new tools, the accessible region grows. -/
structure Boundary where
current : CTerm -- AccessClassifier value (current state)
expansion : Cell -- transport: current → new state after absorption
```
The transport of the boundary cell IS the process of absorbing a new tool. When you perform a pullback to integrate wgpu, you are applying a transport to the boundary cell that moves some cells from `exists` to `accessible` or `compiled`. The evolution of the boundary is a path in the space of topologies on the topos, which is itself a transport, which is itself a cell.
---
## 16. Hardware and Kernel as Sealed Cells
### 16.1 The Sealed Cell Tower
The computational infrastructure beneath the cell system forms a tower, each level a cell, each boundary a trust topology:
```
Level 7: Cell calculus (your system)
↑ guard: the type checker
↑ topology: j_cell
Level 6: Lean 4 runtime
↑ guard: the compiler
↑ topology: j_language
Level 5: Userspace process
↑ guard: the kernel's syscall handler
↑ topology: j_userspace
Level 4: Kernel
↑ guard: interrupt handlers, MMU
↑ topology: j_kernel
Level 3: ISA (instruction set architecture)
↑ guard: decoder, retirement unit
↑ topology: j_isa
Level 2: Microarchitecture
↑ guard: fabrication masks
↑ topology: j_microarch
Level 1: Physics (transistors, electrons)
↑ guard: the laws of physics
↑ topology: j_physics
```
Each level is a cell. Each boundary is a guard cell. Each topology says what's visible from above. The whole tower is a cosimplicial object — a sequence of cells with face maps (projections that forget a level) and degeneracy maps (insertions that add a vacuous level).
### 16.2 Registers as Cells
A CPU register is the simplest hardware cell. Its transport maps "value written" to "value read," but this transport passes through time (clock cycles) and through hazards (pipeline forwarding, speculative execution rollback).
```lean
-- Cells/Boundary/Hardware.lean
/-- A register cell: transport from write-time to read-time. -/
structure RegisterCell where
index : RegId
wordType : MachineType -- .u64, .f64, .v128
cell : Cell
/-- Identity when no hazards intervene -/
hazardFree : HazardCondition → cell.asTransport = Cell.id wordType.toCType
/-- Forwarding path when hazards exist -/
forwarded : Hazard → cell.asTransport = forwardingPath
/-- From userspace, registers are opaque modal cells:
you can use them but cannot inspect their transport. -/
def userspaceRegister (r : RegId) : ModalCell where
cell := registerTransport r
level := .accessible
evidence := .accessible (ffiBinding r)
```
### 16.3 ALU Operations as Cells
An ALU operation (integer addition, multiplication, etc.) is a cell whose transport maps `(a, b)` to the result. The implementation — carry-lookahead adder, transistor-level logic — is sealed behind multiple layers of opacity. Each sealing IS a Lawvere-Tierney topology that hides the level below:
```lean
-- Each hardware abstraction level defines a topology
/-- The ISA topology: instructions visible, microarch hidden.
THIS is the ISA guarantee: same instructions, different
implementations, same observable behavior. -/
def j_isa : Topology where
isLocal := fun φ => φ ∈ isaSpecPropositions
/-- The ABI topology: calling convention visible, encoding hidden. -/
def j_abi : Topology where
isLocal := fun φ => φ ∈ abiSpecPropositions
/-- The topology chain -/
-- j_physics ≤ j_microarch ≤ j_isa ≤ j_abi ≤ j_language ≤ j_cell
```
### 16.4 The Kernel as a Mediating Cell
The operating system kernel is a cell that mediates between userspace and hardware. Its transport maps userspace requests to hardware operations, factored through permission checks:
```lean
/-- The kernel cell: mediation between userspace and hardware. -/
structure KernelCell where
syscallType : CType
cell : Cell
/-- The transport factors through permission checking -/
factors : cell.asTransport = permissionCheck ∘ hardwareDispatch ∘ validateArgs
/-- The permission check is a guard cell -/
guard : GuardCell kernelPolicy trustGap sorry
```
### 16.5 Virtual Memory as a Fibration
Virtual memory is a fiber bundle over virtual address space. The base space is the virtual address space. The fiber over each virtual page is the set of physical frames it could map to. The page table is a section — a choice of mapping. The sheaf condition says: adjacent pages with compatible permissions can be mapped to arbitrary physical frames.
```lean
/-- Virtual memory modeled as a sheaf over address space. -/
structure PageTableCell where
virtualSpace : CType -- Fin (2^48)
physicalSpace : CType -- Fin (2^52)
mapping : CTerm -- virtualSpace → physicalSpace
permissions : CTerm -- virtualSpace → Permission
/-- The security property: process isolation -/
isolation : ∀ pid₁ pid₂, pid₁ ≠ pid₂ →
disjoint (mapping pid₁) (mapping pid₂)
```
Process isolation is the sheaf condition that two processes' sections of the address fibration don't interfere. A kernel exploit that breaks isolation is a proof that the sheaf condition fails.
### 16.6 The Hardware Model from the Cell System
The cell system models the entire sealed tower as pro-cells and opaque modal cells:
```lean
/-- The hardware beneath the cell system. -/
structure HardwareModel where
isa : CellSpec -- ISA-level: what you can see
microarch : ProCell -- microarch: what you guess
physics : ProCell -- physics: pure speculation
trustISA : AccessLevel := .compiled
trustSilicon : AccessLevel := .exists
/-- The kernel beneath the cell system. -/
structure KernelModel where
syscalls : Array CellSpec -- syscall interface
scheduler : ProCell -- scheduling: opaque
mmu : ProCell -- memory management: opaque
trustLevel : AccessLevel := .accessible
```
---
## 17. Security as Cell Topology
### 17.1 Vulnerabilities as Topological Failures
The accessibility topology gives every class of vulnerability a precise type-theoretic description:
A **buffer overflow** is a cell whose transport writes past its declared output boundary — the fiber over the output port is larger than the type claims.
A **privilege escalation** is a transport that crosses a guard cell without passing through the guard — the factorization property of the kernel cell is violated.
A **side channel** is information leaking through a topology — a proposition that should be `j`-hidden is actually `j`-observable. The sheaf condition for the hiding topology fails.
A **sandbox escape** is a section of the accessibility sheaf that extends past its declared boundary — a cell at level `accessible` behaves as though it's at level `compiled` for resources outside its sandbox.
Each of these can be stated as a formal property of the cell graph, and the security policy's guard cells are the mechanisms that prevent them.
### 17.2 Security Properties as Theorems
```lean
-- Cells/Boundary/Security.lean
/-- No data crosses a trust boundary without passing through a guard. -/
theorem guard_completeness (g : CellGraph) (policy : SecurityPolicy) :
∀ w ∈ g.wires,
accessLevel (g.portCell w.source) > accessLevel (g.portCell w.target) →
∃ guard ∈ g.cells, isGuardOn guard w :=
sorry
/-- A guard cell at level `deny` allows no data through. -/
theorem deny_is_empty (guard : GuardCell p .deny) :
∀ input, guard.cell.asTransport input = ⊥ :=
sorry
/-- Process isolation: disjoint address spaces
cannot observe each other's state. -/
theorem process_isolation (pt : PageTableCell)
(pid₁ pid₂ : ProcessId) (h : pid₁ ≠ pid₂) :
∀ (obs : Observable),
obs ∈ visibleTo pid₁ pt →
obs ∉ visibleTo pid₂ pt :=
sorry
/-- The ISA sheaf condition: if the microarchitecture satisfies
the ISA spec, then ISA-level observations are invariant
under microarchitectural change. -/
theorem isa_sheaf_condition (impl₁ impl₂ : MicroarchImpl)
(h₁ : satisfiesISA impl₁) (h₂ : satisfiesISA impl₂) :
∀ (prog : Program) (obs : ISAObservable),
observe obs (run impl₁ prog) = observe obs (run impl₂ prog) :=
sorry
-- When this theorem FAILS for a specific obs,
-- that obs is a side channel (Spectre, Meltdown, etc.)
```
### 17.3 Trust-Annotated Cell Graphs
A workspace with security is a cell graph where every wire is annotated with its trust level and every cross-boundary wire has a guard:
```lean
/-- A security-aware cell graph. -/
structure SecureCellGraph extends CellGraph where
/-- Access level for each cell -/
levels : Array AccessLevel
levels_size : levels.size = cells.size
/-- Every cross-boundary wire has a guard -/
guarded : ∀ i, i < wires.size →
let w := wires[i]!
let srcLevel := levels[w.source.cellId]!
let tgtLevel := levels[w.target.cellId]!
srcLevel ≠ tgtLevel →
∃ g, g < cells.size ∧ isGuardBetween cells[g]! w
/-- The security policy in effect -/
policy : SecurityPolicy
/-- All guards conform to the policy -/
policyConformance : ∀ g, isGuard cells[g]! →
guardConformsToPolicy cells[g]! policy
/-- Check if adding a new wire would violate the security policy. -/
def SecureCellGraph.canConnect (g : SecureCellGraph)
(src tgt : Port) : Bool :=
let srcLevel := g.levels[src.cellId]!
let tgtLevel := g.levels[tgt.cellId]!
if srcLevel == tgtLevel then true
else
let gap := accessGap srcLevel tgtLevel
gap ≤ g.policy.maxGap &&
g.policy.guardFor gap sorry != .deny
```
---
## 18. Expanding the Cell Space — The Grothendieck Construction
### 18.1 Tools as a Diagram
As the system absorbs more external capabilities, each tool defines a "spoke" in a diagram:
```
CellTypes (current)
/ | | \
/ | | \
/ | | \
WGPU Audio Physics Editor
| | | |
↓ ↓ ↓ ↓
GPUOps SndOps SimOps IOOps
```
The total system at any point is the wide pullback (limit) over all spokes. In ∞-categorical language, this is the homotopy limit of the diagram.
### 18.2 The Grothendieck Fibration
The long-term architecture is a Grothendieck fibration. The base category is `Tools` (wgpu, audio, physics, network, editor, etc.). The fiber over each tool is the category of cell types that interface with it. The total category is the full system.
Sections of this fibration are programs — a coherent choice of cells, one for each tool the program uses, such that the compositions agree. The cell graph (workspace) IS a section. The type system enforces that sections are coherent.
The expansion process — absorbing a new tool — is extending the base category by one object and constructing the fiber over it via right Kan extension:
```lean
-- Cells/Boundary/Expansion.lean
/-- A tool integration: the data needed to absorb an
external capability into the cell system. -/
structure ToolIntegration where
/-- Name of the tool -/
name : String
/-- The tool's native type system -/
nativeTypes : List CType
/-- FFI bindings (Rust functions exposed via C ABI) -/
bindings : List FFIBinding
/-- The pullback: cell types that map to native types -/
cellTypes : List CType
/-- Agreement: each cell type compiles to the native type -/
agreement : ∀ i, i < cellTypes.length →
compilesTo cellTypes[i]! nativeTypes[i]!
/-- New cell types constructed by Kan extension:
native capabilities without existing cell counterparts -/
extensions : List CType
/-- Evidence that extensions faithfully represent
the native capabilities -/
extensionSpec : ∀ i, i < extensions.length →
∃ native, faithfulRepresentation extensions[i]! native
/-- Apply a tool integration: expand the cell graph
to include the new tool's capabilities. -/
def expandCellSpace (g : SecureCellGraph)
(tool : ToolIntegration) : SecureCellGraph :=
-- 1. Add the pullback cells (known correspondence)
-- 2. Add the Kan extension cells (new capabilities)
-- 3. Add guard cells at the trust boundary
-- 4. Set access levels for new cells
-- 5. Verify policy conformance
sorry
/-- The wgpu integration as a concrete example. -/
def wgpuIntegration : ToolIntegration where
name := "wgpu"
nativeTypes := [
.base "WGPUDevice",
.base "WGPURenderPipeline",
.base "WGPUBuffer",
.base "WGPUTexture",
.base "WGPUComputePipeline"
]
bindings := [
ffi "cells_wgpu_create_device",
ffi "cells_wgpu_create_pipeline",
ffi "cells_wgpu_create_buffer",
-- ...
]
cellTypes := [
.base "GPUContext", -- maps to WGPUDevice
.base "ShaderHandle", -- maps to WGPURenderPipeline
-- ...
]
agreement := sorry
extensions := [
.base "ComputeCell", -- Kan extension: no prior cell type
.base "StorageBuffer", -- Kan extension: new capability
]
extensionSpec := sorry
```
### 18.3 The Complete Boundary Object
The full mathematical object encoding the cell system's relationship to everything outside it:
```
(Sh_{j_policy}(Ctx), ∂, {j_level}_{level ∈ AccessLevel}, ∫F)
```
where:
- `Ctx` is the category of computational contexts
- `j_policy` is the active security policy topology
- `∂ ∈ Sh_{j_policy}(Ctx)` is the boundary classifier (itself a cell)
- `{j_level}` is the lattice of accessibility topologies
- `∫F` is the Grothendieck construction over the tool diagram `F : Tools^op → Cat`
The cells you have are the `j_verified`-sheaves. The cells you're absorbing are moving from `j_exists`-sheaves to `j_accessible`-sheaves via pullback + Kan extension. The cells at the boundary are presheaves that aren't yet sheaves for your current topology. The security model is the choice of topology. The evolution of the boundary is a path in the space of topologies — itself a transport, itself a cell. It is cells all the way up, and the sealed hardware tower is cells all the way down.
---
## 19. Notation and DSL (Future)
The `Meta/DSL.lean` module will provide syntax extensions for ergonomic cell definition. Target notation:
```lean
-- Define a cell with sugared syntax
cell gradient : Color.sRGB ↔ Color.sRGB :=
transport (i : 𝕀) =>
Color.mix (Color.red) (Color.blue) (Dim.toFloat i)
-- Compose cells with ∘
cell pipeline := geometry ∘ lighting ∘ tonemapping
-- Parallel composition with ×
cell stereo := left_eye × right_eye
-- 2-cell (deformation) with ≃
deform fade : shader_a ≃ shader_b :=
homotopy (i j : 𝕀) =>
Color.mix (shader_a @ i) (shader_b @ i) (Dim.toFloat j)
-- Interactive manipulation
interactive myShader :=
manip (noise_cell >>= color_map_cell)
project: rasterize
deform: closest_param
```
---
## 20. Glossary
| Term | Definition in this system |
|------|--------------------------|
| **Cell** | A transport: a line of types `(i : 𝕀) ⊢ A(i) type` with its induced map `A(0) → A(1)`. |
| **Transport** | The map `A(0) → A(1)` induced by a line of types. Synonymous with "cell." |
| **Fiber** | Given a projection `π : X → S`, the fiber over `s ∈ S` is `{x : X | π(x) = s}`. Mouse position selects a fiber. |
| **Projection** | A map from a computational cell to screen space. Rendering is evaluation of a projection. |
| **Section** | A continuous choice of one fiber element for each screen point. A rendered image is a section. A program is a section of the Grothendieck fibration over tools. |
| **Composition** | Path concatenation. Sequential wiring of cells. Monadic bind. |
| **Glue type** | The type former that constructs paths between equivalent types, giving univalence. |
| **Face formula** | A proposition constraining dimension variables, defining on which faces of a cube a partial element is specified. |
| **hcomp** | Homogeneous composition: filling an open box at a fixed type. |
| **transp** | Transport: moving a value from one end of a type line to the other. |
| **ShaderIR** | A restricted intermediate representation of cell computations that maps to GPU shader languages. |
| **Workspace** | A cell graph: a network of cells connected by wires. The top-level object of the system. |
| **AccessLevel** | A grade in the lattice of accessibility topologies, from `verified` (fully proven) to `unknown` (beyond the border). |
| **ModalCell** | A cell annotated with its accessibility level and the evidence appropriate to that level. |
| **GuardCell** | A cell sitting on a trust boundary whose transport validates data crossing between access levels. |
| **ProCell** | A cell represented by a tower of increasingly refined approximations, for capabilities beyond the accessible border. |
| **Boundary** | The frontier of the accessibility classifier — where the access level changes. Itself a cell whose transport is tool absorption. |
| **Lawvere-Tierney topology** | An endomorphism `j : Ω → Ω` on the subobject classifier selecting which propositions about cells count as "locally true." Different topologies define different trust levels. |
| **Sealed cell** | A cell whose interior cannot be inspected or deformed from the current access level — registers, ALU ops, kernel internals. |
| **Sheaf condition** | The requirement that local data (individual cell behaviors) agreeing on overlaps (shared state) glue to global data (program behavior). Violation = vulnerability. |
| **Pullback** | The universal type mapping into both the cell calculus and an external tool's type system, agreeing on shared semantics. The basic move for absorbing a new tool. |
| **Kan extension** | The construction of new cell types to cover external capabilities that have no existing cell counterpart. How the cell system grows. |
| **Grothendieck fibration** | The total system: a fibration over the category of tools, whose fiber over each tool is the cell types that interface with it. Sections are programs. |
---
*This document is the complete specification for the Cells system as a Lean 4 extension. It depends on nothing outside itself, Lean 4's `Init`, and the Rust standard library (reached via a C ABI FFI). Everything described here — from the de Morgan interval algebra through the sealed cell tower of hardware and kernel, to the Grothendieck fibration of tool absorption — is buildable, testable, and maintainable by a single developer. The process discipline is absolute: every layer is formalized in Lean as axioms before the Rust FFI discharges them. The boundary is a cell. The expansion of the boundary is a transport. It is cells all the way down, and cells all the way up.*