The math

Not a style choice.
A consequence.

flowG’s claims aren’t aesthetic. They fall out of five well-understood results — from information theory, computation theory, type theory, and physics. Here is the reasoning, including where a claim is a theorem and where it’s an empirical bet.

01 — Information theory

Text is a structure-implicit projection of a graph.

A program is a typed graph G = (V, E): nodes are operations and values, edges carry how those values relate. Writing it to a file is a serialization s : G → Σ* — a map onto a one-dimensional string. Compiling is the inverse p : Σ* → G.

// the round trip a compiler runs on every build
source text  ──parse──▶  tokens
  ──name resolution──▶  bindings (which definition does this name mean?)
  ──type inference──▶  types (what is the type of this expression?)
  ──flow analysis──▶  data & control edges
  ──borrow / lifetime──▶  ownership (who owns, who borrows)
  = G // the graph the author already had in mind

Two facts make text the wrong canonical form. First, it is redundant: many strings denote the same graph — whitespace, ordering, and names form equivalence classes with no semantic content, so s is not injective in reverse. Second, it is structure-implicit: the relations the author knew — bindings, types, ownership, data and control flow — are not written down. The compiler re-derives them on every build. That re-derivation is the reconstruction of G.

flowG stores G — the canonical, structure-explicit form — and derives text on demand. The lossy step doesn’t disappear; it moves to the edge of the system, where it belongs.

02 — Computation theory

A closed kernel of 22 primitives.

The structured program theorem (Böhm–Jacopini, 1966) showed that all computable control flow reduces to three forms: sequence, selection, iteration. flowG extends that spine across the other axes a real program needs — data, resources, errors, concurrency, types, and time — and closes the set at 22.

Computation · 10
Bind · Apply · Mutate · Branch · Iterate · Match · Sequence · Compose · Abstract · Import
Resource · 3
Acquire · Release · Observe
Error · 2
ErrorPropagate · CodeBlock
Concurrency · 4
Spawn · JoinAwait · YieldSuspend · Listen
Data / State · 2
Transact · TypeDefine
Temporal · 1
FeedbackDelay
Theorem vs. bet — said plainly

Turing-completeness from a small set is trivial and proves nothing interesting. The real claim is ergonomic completeness: that these 22 express every pattern across paradigms without awkward encodings. That is an empirical bet, not a theorem — and the evidence for it is the omni-language round trip: parse real code in any language with a tree-sitter grammar into these primitives, and materialize it back.

03 — Determinism

Materialization is a pure function.

Emitting a language is a referentially transparent function of the graph and the target — no hidden state, no sampling, no model in the loop:

emit : Graph × LanguageText
Deterministic

Same graph, same output — byte for byte. No temperature, no seed.

Idempotent

Re-materializing changes nothing. There is nothing to “regenerate.”

Drift-free

All N projections are emit(G, Lᵢ) of one G — so they cannot disagree.

“Zero drift across languages” is therefore not a process discipline you have to maintain — it’s a property of pure functions. Change G; every projection follows by construction. (This very site is built the same way — one canon.json, every page a projection of it.)

04 — Type theory

Wires are a substructural type algebra.

Each of the 11 wire kinds is a typing rule on how a value passes from producer to consumer — the same substructural type theory (Girard’s linear logic; Wadler, “linear types can change the world”) that underlies Rust’s ownership, lifted onto the edge itself.

Movelinear — consumed exactly once
Borrownon-consuming read; source retained
MutBorrowexclusive — at most one at a time
Copyunrestricted — freely duplicable
Shared / Weakcounted ownership; cycle-breaking
Stream / Lazy / Feedbacktemporal & deferred discipline
Whole bug classes become structurally impossible
  • Use-after-move — a Move consumes the source; it has no output port to wire from. The graph can’t express it.
  • Data raceMutBorrow is exclusive; a second mutable edge to the same node is rejected at edit time, not run time.
  • Deadlock — circular Acquire chains are caught by cycle detection (Kahn’s topological sort, 1962).
05 — Physics

Energy is a first-class cost.

Every operation carries a picojoule cost. We model it as a linear function of the work it does, fit from measured silicon — and crucially, data movement dominates arithmetic by orders of magnitude (Horowitz, 2014):

E(op)  ≈  a·flops  +  b·bytes  +  c
with   b ≫ a   — moving the data costs far more than the math

Placement is then an optimization: route each op to the backend that minimizes estimated joules, preferring measured calibration over the analytical prior.

place(op) = argminbackend  Ê(op, backend)   ·   receipt = Σ E(op)

This is not theory on a slide. On real hardware this session, a resident decode reported a whole-SoC receipt of ~445 mJ/token, and the linear coefficients above were fit by ordinary least squares from measured joules — confirming, as the model predicts, that weight movement (the b·bytes term) dominates the energy.

Five results, one object.

The graph is canonical, the kernel is closed, materialization is pure, the wires are typed, and every op is costed in joules. None of these are bolted together — they’re five views of a single structure. That’s why flowG is a substrate and not a feature.