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