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

98 KiB
Raw Blame History

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.

-- 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.

-- 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.

-- 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.

-- 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
-- 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.

-- 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.

-- 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.

-- 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

-- 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

-- 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

-- 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

-- 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

-- 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

-- 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

-- 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

-- 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

-- 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

-- 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

-- 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)

-- 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

-- 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

-- 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

-- 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)

// 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)

// 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)

// 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

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_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:

-- 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:

-- 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.

-- 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.

-- 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).

-- 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:

-- 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:

/-- 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.

/-- 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:

/-- 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

-- 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:

/-- 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:

-- 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:

-- 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
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.