Some checks are pending
Lean Action CI / build (push) Waiting to run
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>
188 lines
7.5 KiB
Text
188 lines
7.5 KiB
Text
/-
|
||
Topolei.Trace
|
||
=============
|
||
H5 of the foundation stack: the **trace map** (inverse projection),
|
||
in its polymorphic form.
|
||
|
||
## Polymorphic from day 1
|
||
|
||
A `Trace α` is a list of contributing items of type `α`. The
|
||
parameter `α` is what the trace is *about*:
|
||
|
||
- `Trace WCell` — workspace-tree-level traces (rendered → workspace cells)
|
||
- `Trace CTerm` — cubical-syntax-level traces (rendered → CTerm sub-terms)
|
||
- `Trace SourceLoc` — file/line provenance traces
|
||
- `Trace HandleId` — GPU resource provenance
|
||
|
||
All four are the same algebraic structure (a free monoid on `α`) with
|
||
the same theorems. Polymorphism captures the "consolidate
|
||
abstractions" principle: ONE Trace type, instantiated wherever
|
||
needed.
|
||
|
||
## Why this layer
|
||
|
||
H1+H2 (Selection) gave us *focus + history* — pointing at one cell
|
||
with a path back to the root. H3 (Subobject) gave us the *algebra
|
||
of scopes*. C2 (Obs.Ctx) gave us the typed peripheral category.
|
||
|
||
H5 closes the loop: every rendered element carries a typed pointer
|
||
back to the source items that contributed to producing it. This is
|
||
the **inverse projection**: given an output (a pixel, region, curve
|
||
on the screen), recover the items whose values projected to it.
|
||
|
||
## Geometry, sheaves, bundles, differential structure: ALL derived
|
||
|
||
Differential geometry is NOT a separate framework bolted onto cells.
|
||
It is a *property of trace coherence* — particular shapes of how
|
||
`Trace`s relate across the rendered space:
|
||
|
||
- Trace varies smoothly across adjacent rendered elements →
|
||
differential structure (Jacobian / connection / parallel transport).
|
||
- Trace shares open sets in coherent overlaps → sheaf structure.
|
||
- Multiple sources project to one rendered point → fiber bundle's
|
||
preimage at that point.
|
||
|
||
The same `Trace α` carries all three; the geometry is in the
|
||
*predicates* / *projections* over traces, not in the type itself.
|
||
|
||
## What this file does NOT contain
|
||
|
||
- **The cubical-trace function** `CTerm → Trace CTerm`. That's a
|
||
sibling file `Topolei.Cubical.Trace`, which uses *this* `Trace`.
|
||
- **DecidableEq-dependent operations** (diff, intersect). Add when
|
||
needed; they require `DecidableEq α`.
|
||
|
||
## Reference
|
||
|
||
Cells-spec §1.5 ("Rendering Context as a Cell"), §17.1
|
||
("Vulnerabilities as Topological Failures": side channels are
|
||
unwanted traces — same abstraction, security layer).
|
||
-/
|
||
|
||
namespace Topolei.Trace
|
||
|
||
-- ── The Trace structure ───────────────────────────────────────────────────
|
||
|
||
/-- A trace: the typed list of items that contributed to producing a
|
||
rendered element. Polymorphic in the item type — instantiate
|
||
with `WCell` for workspace-tree traces, `CTerm` for cubical-
|
||
syntax traces, etc. Plural because at singularities, multiple
|
||
sources project to one rendered point — and even at non-singular
|
||
points the trace can include intermediate items (the sub-things
|
||
visited en route to the final output).
|
||
|
||
The list order records *contribution order*: the first item is
|
||
the deepest contributor, the last is the most recently seen. This
|
||
order matters when stacking traces during rendering. -/
|
||
structure Trace (α : Type) where
|
||
items : List α
|
||
deriving Repr, Inhabited
|
||
|
||
namespace Trace
|
||
|
||
/-- The empty trace — no items contributed. Identity element for
|
||
`union`, witness that some rendered element has no source-item
|
||
provenance (e.g., a clear-color background pixel). -/
|
||
def empty {α : Type} : Trace α := { items := [] }
|
||
|
||
/-- The single-item trace — exactly one source-item contributed.
|
||
Used when lifting a primitive into a traced render. -/
|
||
def single {α : Type} (a : α) : Trace α := { items := [a] }
|
||
|
||
/-- Combine two traces by concatenating their item lists. Order is
|
||
preserved: the second trace's items append after the first's.
|
||
This is *not* deduplicating — if an item appears in both traces
|
||
we keep both occurrences (the list is multi-set-like).
|
||
Deduplication is a separate operation that requires `DecidableEq`. -/
|
||
def union {α : Type} (t₁ t₂ : Trace α) : Trace α :=
|
||
{ items := t₁.items ++ t₂.items }
|
||
|
||
-- ── Monoid laws (empty is unit, union is associative) ────────────────────
|
||
|
||
@[simp] theorem empty_union {α : Type} (t : Trace α) :
|
||
Trace.empty.union t = t := by
|
||
cases t; simp [union, empty]
|
||
|
||
@[simp] theorem union_empty {α : Type} (t : Trace α) :
|
||
t.union Trace.empty = t := by
|
||
cases t; simp [union, empty]
|
||
|
||
theorem union_assoc {α : Type} (t₁ t₂ t₃ : Trace α) :
|
||
(t₁.union t₂).union t₃ = t₁.union (t₂.union t₃) := by
|
||
cases t₁; cases t₂; cases t₃
|
||
simp [union, List.append_assoc]
|
||
|
||
end Trace
|
||
|
||
-- ── TracedRender: rendered element + its trace ────────────────────────────
|
||
|
||
/-- A `TracedRender R α` is a rendered element of type `R` paired
|
||
with its `Trace α`. Every rendered output carries its provenance.
|
||
|
||
`R` is the rendered-element type (e.g. `PixelColor`, a region
|
||
label, a frame). `α` is the trace-item type (e.g. `WCell`,
|
||
`CTerm`, …). Polymorphism lets the same abstraction work for
|
||
every rendering granularity and every provenance shape. -/
|
||
structure TracedRender (R α : Type) where
|
||
render : R
|
||
trace : Trace α
|
||
deriving Repr, Inhabited
|
||
|
||
namespace TracedRender
|
||
|
||
/-- Lift a rendered value with a single-item trace. -/
|
||
def lift {R α : Type} (r : R) (a : α) : TracedRender R α :=
|
||
{ render := r, trace := Trace.single a }
|
||
|
||
/-- Lift a rendered value with no trace — for outputs without source-
|
||
item provenance (clear color, vertex-stage outputs, sealed-cell
|
||
rasterizer products). -/
|
||
def liftEmpty {R α : Type} (r : R) : TracedRender R α :=
|
||
{ render := r, trace := Trace.empty }
|
||
|
||
/-- Combine two traced renders by combining their traces (left-biased
|
||
on the rendered value). -/
|
||
def merge {R α : Type} (a b : TracedRender R α) : TracedRender R α :=
|
||
{ render := a.render, trace := a.trace.union b.trace }
|
||
|
||
@[simp] theorem merge_liftEmpty {R α : Type} (a : TracedRender R α) (r' : R) :
|
||
a.merge (liftEmpty r') = a := by
|
||
cases a; simp [merge, liftEmpty, Trace.union_empty]
|
||
|
||
@[simp] theorem liftEmpty_merge {R α : Type} (r : R) (b : TracedRender R α) :
|
||
(liftEmpty r).merge b = { render := r, trace := b.trace } := by
|
||
simp [merge, liftEmpty, Trace.empty_union]
|
||
|
||
end TracedRender
|
||
|
||
-- ── Operational sanity (polymorphic on `String` items as the simplest demo) ─
|
||
|
||
/-- Demo trace over `String` items. -/
|
||
def demoTrace : Trace String :=
|
||
(Trace.single "alpha").union (Trace.single "beta")
|
||
|
||
#eval demoTrace.items.length -- expected: 2
|
||
#eval demoTrace.items -- expected: ["alpha", "beta"]
|
||
|
||
/-- Empty-union round-trip. -/
|
||
example : (Trace.empty : Trace String).union demoTrace = demoTrace :=
|
||
Trace.empty_union _
|
||
|
||
/-- Union associativity exercised on three traces. -/
|
||
example :
|
||
let t₁ := (Trace.single "a" : Trace String)
|
||
let t₂ := Trace.single "b"
|
||
let t₃ := Trace.empty
|
||
(t₁.union t₂).union t₃ = t₁.union (t₂.union t₃) :=
|
||
Trace.union_assoc _ _ _
|
||
|
||
/-- `TracedRender` lift carries the trace it's given. -/
|
||
example : (TracedRender.lift (5 : Nat) "src").trace = Trace.single "src" := rfl
|
||
|
||
/-- Merging into a no-trace render leaves the trace alone. -/
|
||
example :
|
||
let a : TracedRender Nat String := TracedRender.lift 5 "src"
|
||
a.merge (TracedRender.liftEmpty 7) = a :=
|
||
TracedRender.merge_liftEmpty _ _
|
||
|
||
end Topolei.Trace
|