git » blacktide.git » commit 45a5142

Organize compiler languages and lexical processor groups

author Alan Dipert
2026-11-06 21:24:12 UTC
committer Alan Dipert
2026-11-06 21:24:12 UTC
parent dfe76d51482f7833f2b695e927c2fc3aa7d0509b

Organize compiler languages and lexical processor groups

doc/compiler.md +11 -2
doc/nanopass.md +8 -2
src/com/tailrecursion/blacktide/compiler.clj +54 -23
src/com/tailrecursion/blacktide/compiler/analysis_languages.clj +0 -23
src/com/tailrecursion/blacktide/compiler/assignment.clj +0 -123
src/com/tailrecursion/blacktide/compiler/backend.clj +0 -26
src/com/tailrecursion/blacktide/compiler/boundaries.clj +0 -70
src/com/tailrecursion/blacktide/compiler/cases.clj +0 -24
src/com/tailrecursion/blacktide/compiler/collections.clj +0 -71
src/com/tailrecursion/blacktide/compiler/compact_languages.clj +0 -24
src/com/tailrecursion/blacktide/compiler/effects.clj +0 -28
src/com/tailrecursion/blacktide/compiler/errors.clj +5 -5
src/com/tailrecursion/blacktide/compiler/jvm.clj +1 -1
src/com/tailrecursion/blacktide/compiler/jvm_languages.clj +0 -35
src/com/tailrecursion/blacktide/compiler/languages.clj +200 -14
src/com/tailrecursion/blacktide/compiler/lower.clj +0 -141
src/com/tailrecursion/blacktide/compiler/native.clj +0 -108
src/com/tailrecursion/blacktide/compiler/order.clj +0 -116
src/com/tailrecursion/blacktide/compiler/passes/assignment.clj +123 -0
src/com/tailrecursion/blacktide/compiler/passes/boundaries.clj +76 -0
src/com/tailrecursion/blacktide/compiler/{ => passes}/box_origins.clj +3 -4
src/com/tailrecursion/blacktide/compiler/{ => passes}/case_emit.clj +2 -3
src/com/tailrecursion/blacktide/compiler/passes/cases.clj +25 -0
src/com/tailrecursion/blacktide/compiler/passes/collections.clj +69 -0
src/com/tailrecursion/blacktide/compiler/{ => passes}/compact.clj +73 -74
src/com/tailrecursion/blacktide/compiler/passes/effects.clj +27 -0
src/com/tailrecursion/blacktide/compiler/{ => passes}/emit.clj +5 -5
src/com/tailrecursion/blacktide/compiler/{ => passes}/feasible.clj +1 -1
src/com/tailrecursion/blacktide/compiler/{ => passes}/graph.clj +2 -2
src/com/tailrecursion/blacktide/compiler/{ => passes}/host_emit.clj +4 -5
src/com/tailrecursion/blacktide/compiler/{ => passes}/invariants.clj +4 -4
src/com/tailrecursion/blacktide/compiler/{ => passes}/joins.clj +12 -13
src/com/tailrecursion/blacktide/compiler/{ => passes}/loop_facts.clj +77 -78
src/com/tailrecursion/blacktide/compiler/passes/lower.clj +139 -0
src/com/tailrecursion/blacktide/compiler/passes/native.clj +104 -0
src/com/tailrecursion/blacktide/compiler/passes/order.clj +115 -0
src/com/tailrecursion/blacktide/compiler/passes/prepare.clj +44 -0
src/com/tailrecursion/blacktide/compiler/passes/source.clj +410 -0
src/com/tailrecursion/blacktide/compiler/{ => passes}/source_case.clj +6 -6
src/com/tailrecursion/blacktide/compiler/{ => passes}/source_host.clj +3 -4
src/com/tailrecursion/blacktide/compiler/{ => passes}/source_jvm.clj +6 -7
src/com/tailrecursion/blacktide/compiler/passes/source_values.clj +38 -0
src/com/tailrecursion/blacktide/compiler/{ => passes}/storage.clj +13 -15
src/com/tailrecursion/blacktide/compiler/passes/structure.clj +160 -0
src/com/tailrecursion/blacktide/compiler/passes/structured_emit.clj +108 -0
src/com/tailrecursion/blacktide/compiler/{ => passes}/transfers.clj +1 -1
src/com/tailrecursion/blacktide/compiler/{ => passes}/value_facts.clj +13 -15
src/com/tailrecursion/blacktide/compiler/prepare.clj +0 -46
src/com/tailrecursion/blacktide/compiler/source.clj +0 -412
src/com/tailrecursion/blacktide/compiler/source_languages.clj +0 -93
src/com/tailrecursion/blacktide/compiler/source_values.clj +0 -36
src/com/tailrecursion/blacktide/compiler/structure.clj +0 -163
src/com/tailrecursion/blacktide/compiler/structured_emit.clj +0 -110
src/com/tailrecursion/blacktide/compiler/structured_languages.clj +0 -34
src/com/tailrecursion/nanopass.clj +37 -10
src/com/tailrecursion/nanopass/pass.clj +107 -110
test/com/tailrecursion/blacktide/annotations.clj +25 -121
test/com/tailrecursion/blacktide/backend_test.clj +12 -12
test/com/tailrecursion/blacktide/check.clj +20 -50
test/com/tailrecursion/blacktide/collections_test.clj +4 -4
test/com/tailrecursion/blacktide/generated_test.clj +2 -2
test/com/tailrecursion/blacktide/graph_test.clj +1 -1
test/com/tailrecursion/blacktide/machine_test.clj +1 -1
test/com/tailrecursion/blacktide/source_loops_test.clj +4 -4
test/com/tailrecursion/nanopass/check.clj +22 -0
test/com/tailrecursion/nanopass/clients/assignment.clj +20 -21
test/com/tailrecursion/nanopass/clients/flow.clj +74 -74
test/com/tailrecursion/nanopass/pass_test.clj +4 -1
test/com/tailrecursion/nanopass/passes.clj +23 -25

diff --git a/doc/compiler.md b/doc/compiler.md
index 19b48b1..dea52a6 100644
--- a/doc/compiler.md
+++ b/doc/compiler.md
@@ -5,6 +5,11 @@ Blacktide transforms immutable Clojure data through languages defined with
 checker is absent from ordinary library use. Namespace docstrings state pass
 contracts. Compiler namespaces and intermediate representations are internal.
 
+All grammars live in [`compiler/languages.clj`](../src/com/tailrecursion/blacktide/compiler/languages.clj).
+Transformations and their helpers live under [`compiler/passes/`](../src/com/tailrecursion/blacktide/compiler/passes/).
+The executable pipeline, including the backend choice, lives in
+[`compiler.clj`](../src/com/tailrecursion/blacktide/compiler.clj).
+
 | Stage | Responsibility |
 | --- | --- |
 | Source | Expand macros in order, resolve unique lexical identities and source JVM facts, enforce the subset, solve loop representations without repeating expansion. |
@@ -20,8 +25,10 @@ contracts. Compiler namespaces and intermediate representations are internal.
 
 The outer bindings enter as one lexical scope; emission seals the result with an
 Object type boundary. `Native` contains analyzed subtrees without DSL operations.
-The structured backend uses `Machine → Planned → Joined → Stored → Compact → Code`
-and shares graph validation with the dispatcher.
+The structured backend uses `Machine → Analysis → Planned → Joined → Stored → Compact → Code`
+and shares graph validation with the dispatcher. Grammar derivation expresses
+structural relationships, not execution order: `Symbolic` extends `CFG`, which
+extends `Machine`, though lowering visits them in the opposite order.
 
 Assignments, normalization and graph construction are pure. Locals become
 lexical bindings and loop parameters, never mutable cells. A generated
@@ -36,6 +43,8 @@ Clojure analysis. The Java helper targets JDK 17 and is bundled in JARs.
 
 To extend the compiler, change the language and affected passes, then run
 `bin/check`. Prefer a compiler-local transformation over new nanopass machinery.
+Checking discovers compiler source namespaces and registers language and pass
+contracts from their metadata; new files need no separate registration list.
 Tests should establish source semantics or a necessary invariant. Generators
 use independent models or ordinary Clojure, deterministic seeds and shrinking;
 backend comparisons supplement those independent references.
diff --git a/doc/nanopass.md b/doc/nanopass.md
index bb1e150..1120b5b 100644
--- a/doc/nanopass.md
+++ b/doc/nanopass.md
@@ -46,8 +46,14 @@ Register `(np/ann-passes lower)` in the checking-only namespace, then check the
 implementation namespace with `typed.clojure/check-ns-clj`. Generated signatures
 are contracts, not a replacement for checking function bodies.
 
-`:processors {Binding lower-binding}` names other nonterminal processors; use
-ordinary `declare` for mutual recursion. `:args [ContextType]` types leading
+`:processors {Binding lower-binding}` names other nonterminal processors. Group
+mutually recursive processors with `np/defpasses`; each entry uses `defpass`
+syntax without the macro name. See the [recursive traversal example](../test/com/tailrecursion/nanopass/passes.clj).
+Expansion produces one
+lexical `letfn` group and exports its functions with their individual signatures.
+The checker checks every body; the group is redefined as a unit.
+
+`:args [ContextType]` types leading
 arguments, which generated traversal forwards unchanged. Write explicit clauses
 when lexical context changes. With `:body true`, supply one ordinary expression
 and any result type in `:to`, including state/result tuples. Automatic clauses
diff --git a/src/com/tailrecursion/blacktide/compiler.clj b/src/com/tailrecursion/blacktide/compiler.clj
index 21507eb..c4e3f66 100644
--- a/src/com/tailrecursion/blacktide/compiler.clj
+++ b/src/com/tailrecursion/blacktide/compiler.clj
@@ -3,41 +3,72 @@
   macro supplies marker Vars and the caller's lexical environment."
   (:require [com.tailrecursion.blacktide.compiler.errors :as errors]
             [com.tailrecursion.nanopass :as np]
-            [com.tailrecursion.blacktide.compiler.source-languages :as s]
             [com.tailrecursion.blacktide.compiler.languages :as l]
-            [com.tailrecursion.blacktide.compiler.source :as source]
-            [com.tailrecursion.blacktide.compiler.source-jvm :as facts]
-            [com.tailrecursion.blacktide.compiler.boundaries :as boundaries]
-            [com.tailrecursion.blacktide.compiler.cases :as cases]
-            [com.tailrecursion.blacktide.compiler.assignment :as assignment]
-            [com.tailrecursion.blacktide.compiler.source-values :as source-values]
-            [com.tailrecursion.blacktide.compiler.collections :as collections]
-            [com.tailrecursion.blacktide.compiler.order :as order]
-            [com.tailrecursion.blacktide.compiler.prepare :as prepare]
-            [com.tailrecursion.blacktide.compiler.lower :as lower]
-            [com.tailrecursion.blacktide.compiler.transfers :as transfers]
-            [com.tailrecursion.blacktide.compiler.graph :as graph]
-            [com.tailrecursion.blacktide.compiler.backend :as backend]
-            [com.tailrecursion.blacktide.compiler.host-emit :as host]
+            [com.tailrecursion.blacktide.compiler.passes.source :as source]
+            [com.tailrecursion.blacktide.compiler.passes.source-jvm :as facts]
+            [com.tailrecursion.blacktide.compiler.passes.boundaries :as boundaries]
+            [com.tailrecursion.blacktide.compiler.passes.cases :as cases]
+            [com.tailrecursion.blacktide.compiler.passes.assignment :as assignment]
+            [com.tailrecursion.blacktide.compiler.passes.source-values :as source-values]
+            [com.tailrecursion.blacktide.compiler.passes.collections :as collections]
+            [com.tailrecursion.blacktide.compiler.passes.order :as order]
+            [com.tailrecursion.blacktide.compiler.passes.prepare :as prepare]
+            [com.tailrecursion.blacktide.compiler.passes.lower :as lower]
+            [com.tailrecursion.blacktide.compiler.passes.transfers :as transfers]
+            [com.tailrecursion.blacktide.compiler.passes.graph :as graph]
+            [com.tailrecursion.blacktide.compiler.passes.feasible :as feasible]
+            [com.tailrecursion.blacktide.compiler.passes.value-facts :as value-facts]
+            [com.tailrecursion.blacktide.compiler.passes.joins :as joins]
+            [com.tailrecursion.blacktide.compiler.passes.structure :as structure]
+            [com.tailrecursion.blacktide.compiler.passes.invariants :as invariants]
+            [com.tailrecursion.blacktide.compiler.passes.storage :as storage]
+            [com.tailrecursion.blacktide.compiler.passes.compact :as compact]
+            [com.tailrecursion.blacktide.compiler.passes.structured-emit :as structured]
+            [com.tailrecursion.blacktide.compiler.passes.emit :as emit]
+            [com.tailrecursion.blacktide.compiler.passes.host-emit :as host]
             [typed.clojure :as-alias t]))
 
-(np/defpass compile-tree {:from [s/Bound Expr] :to [l/Machine Code] :args [s/Bound-Supply] :body true}
+(np/defpass compile-program
+  {:from [l/Machine Program] :to [l/Machine Code] :body true}
+  [machine]
+  (let [cfg (feasible/program {:entry (:entry machine) :blocks (:blocks machine)})
+        reduced (graph/analyze cfg)
+        analysis (value-facts/analyze reduced)
+        planned (joins/plan analysis)
+        decision (structure/recover planned)]
+    (if (= :structured (:op decision))
+      (let [joined (invariants/expression #{} (:body decision))
+            stored (storage/select-storage analysis joined)
+            compact (compact/program stored)]
+        (structured/expression (:types compact) (:body compact)))
+      (emit/program-form machine))))
+
+(np/defpass compile-tree
+  {:from [l/Bound Expr] :to [l/Machine Code] :args [l/Bound-Supply] :body true}
   [supply expr]
-  (let [[supply expr] (assignment/parallel supply (assignment/sequential (source-values/expression (boundaries/classify expr))))
-        [supply expr] (order/normalize supply (prepare/prepare (collections/normalize (cases/normalize expr))))
-        program (graph/analyze (transfers/resolve-program (lower/compile-graph supply expr)))]
-    (host/box-form (backend/compile-program program))))
+  (let [classified (boundaries/classify expr)
+        values (source-values/expression classified)
+        sequential (assignment/sequential values)
+        [supply assigned] (assignment/parallel supply sequential)
+        selected (cases/normalize assigned)
+        constructed (collections/normalize selected)
+        prepared (prepare/prepare constructed)
+        [supply ordered] (order/normalize supply prepared)
+        symbolic (lower/compile-graph supply ordered)
+        cfg (transfers/resolve-program symbolic)
+        machine (graph/analyze cfg)]
+    (host/box-form (compile-program machine))))
 
 (np/defpass caller-locals
-  {:from [s/Bound Context] :to [s/Bound Context] :body true}
+  {:from [l/Bound Context] :to [l/Bound Context] :body true}
   [ctx]
   (assoc ctx :locals
-    (reduce-kv (fn [^{::t/- (t/Map t/Sym s/Bound-Local)} out ^{::t/- t/Sym} name ^{::t/- t/Any} binding]
+    (reduce-kv (fn [^{::t/- (t/Map t/Sym l/Bound-Local)} out ^{::t/- t/Sym} name ^{::t/- t/Any} binding]
                  (assoc out name {:id name :name name :mutable? false :external? true
                                   :jvm (facts/caller-facts name binding)})) {} (:macro-env ctx))))
 
 (np/defpass compile-form
-  {:from [s/Bound Raw] :to [l/Machine Code]
+  {:from [l/Bound Raw] :to [l/Machine Code]
    :args [(t/Map t/Sym t/Any) (t/Map t/AnyVar t/Kw)] :body true}
   [env markers form]
   (let [_ (assert (seq? form) "Expected a loophole macro form")
diff --git a/src/com/tailrecursion/blacktide/compiler/analysis_languages.clj b/src/com/tailrecursion/blacktide/compiler/analysis_languages.clj
deleted file mode 100644
index 9fd8408..0000000
--- a/src/com/tailrecursion/blacktide/compiler/analysis_languages.clj
+++ /dev/null
@@ -1,23 +0,0 @@
-(ns com.tailrecursion.blacktide.compiler.analysis-languages
-  "Value proofs and join plans for checked control-flow graphs. Bottom is an
-  unresolved copy cycle; boxed values have no primitive storage proof."
-  (:require [com.tailrecursion.nanopass :as np]
-            [com.tailrecursion.blacktide.compiler.languages :as l]
-            [typed.clojure :as-alias t]))
-
-(np/deflanguage Analysis
-  {Type (t/U ':bottom ':boxed java.lang.Class)
-   Types (t/Map t/Sym Type)
-   Definitions (t/Map t/Sym (t/Vec l/Machine-Rhs))
-   Program (t/HMap :mandatory {:machine l/Machine-Program :types Types
-                               :reachable (t/Set l/Machine-Label)} :complete? true)})
-
-(np/derive-language Planned Analysis
-  {:replace
-   {Postdominators (t/Map l/Machine-Label (t/Set l/Machine-Label))
-    Region (t/HMap :mandatory {:definite (t/Set t/Sym) :changed (t/Set t/Sym)} :complete? true)
-    Regions (t/Map l/Machine-Label Region)
-    Join (t/HMap :mandatory {:target l/Machine-Label :result t/Sym} :complete? true)
-    Joins (t/Map l/Machine-Label Join)
-    Program (t/HMap :mandatory {:machine l/Machine-Program :types Types
-                                :reachable (t/Set l/Machine-Label) :joins Joins} :complete? true)}})
diff --git a/src/com/tailrecursion/blacktide/compiler/assignment.clj b/src/com/tailrecursion/blacktide/compiler/assignment.clj
deleted file mode 100644
index f79276e..0000000
--- a/src/com/tailrecursion/blacktide/compiler/assignment.clj
+++ /dev/null
@@ -1,123 +0,0 @@
-(ns ^:typed.clojure com.tailrecursion.blacktide.compiler.assignment
-  "Lower sequential batches to writes, then parallel batches to saved RHSs and
-  writes. Pending parallel writes do not execute if an RHS abandons evaluation."
-  (:require [com.tailrecursion.nanopass :as np]
-            [com.tailrecursion.blacktide.compiler.source-languages :as l]
-            [com.tailrecursion.blacktide.compiler.source :as source]
-            [typed.clojure :as-alias t]))
-
-(declare sequential sequential-binding sequential-pair sequential-forms sequential-pairs parallel)
-
-(np/defpass sequential-pair
-  {:from [l/Classified Assignment] :to [l/Sequential Assignment]
-   :processors {Local clojure.core/identity Expr sequential}}
-  [pair])
-
-(np/defpass sequential-pairs
-  {:from [l/Classified Assignments] :to [l/Sequential Assignments] :processors {Assignment sequential-pair}}
-  [pairs])
-
-(np/defpass sequential-forms
-  {:from [l/Classified Exprs] :to [l/Sequential Exprs] :processors {Expr sequential}}
-  [forms])
-
-(np/defpass sequential-node
-  {:from [l/Classified Node] :to [l/Sequential Node]
-   :processors {Local clojure.core/identity Raw clojure.core/identity CollectionKind clojure.core/identity RecurTarget clojure.core/identity CaseSpec clojure.core/identity
-                Expr sequential Exprs sequential-forms Binding sequential-binding Assignments sequential-pairs}}
-  [node]
-  :setq {:op :do :forms
-         (mapv (fn [pair]
-                 (let [value (sequential (:value pair))]
-                   (assoc value :node {:op :write :target (:target pair) :value value}))) (:pairs node))})
-
-(np/defpass sequential-binding
-  {:from [l/Classified Binding] :to [l/Sequential Binding]
-   :processors {Local clojure.core/identity Expr sequential}}
-  [binding])
-
-(np/defpass sequential
-  {:from [l/Classified Expr] :to [l/Sequential Expr] :body true}
-  [expr]
-  (assoc expr :node (sequential-node (:node expr))))
-
-(np/defpass parallel-forms
-  {:from [l/Sequential Exprs] :to (t/HVec [l/Bound-Supply l/Assigned-Exprs])
-   :args [l/Bound-Supply] :body true}
-  [supply forms]
-  (reduce (fn [^{::t/- (t/HVec [l/Bound-Supply l/Assigned-Exprs])} acc ^{::t/- l/Sequential-Expr} expr]
-            (let [[supply out] acc [supply expr] (parallel supply expr)] [supply (conj out expr)]))
-          [supply []] forms))
-
-(np/defpass parallel-bindings
-  {:from [l/Sequential Bindings] :to (t/HVec [l/Bound-Supply l/Assigned-Bindings])
-   :args [l/Bound-Supply] :body true}
-  [supply bindings]
-  (reduce (fn [^{::t/- (t/HVec [l/Bound-Supply l/Assigned-Bindings])} acc ^{::t/- l/Sequential-Binding} binding]
-            (let [[supply out] acc [supply init] (parallel supply (:init binding))]
-              [supply (conj out (assoc binding :init init))])) [supply []] bindings))
-
-(np/defpass save-parallel-values
-  {:from [l/Sequential Assignments]
-   :to (t/HVec [l/Bound-Supply l/Assigned-Bindings l/Assigned-Exprs]) :args [l/Bound-Supply] :body true}
-  [supply pairs]
-  (reduce (fn [^{::t/- (t/HVec [l/Bound-Supply l/Assigned-Bindings l/Assigned-Exprs])} acc
-               ^{::t/- l/Sequential-Assignment} pair]
-            (let [[supply bindings writes] acc [supply value] (parallel supply (:value pair))
-                  [supply id] (source/fresh "parallel" supply)
-                  local {:id id :name id :mutable? false :external? false :jvm (:jvm value)}
-                  read (assoc value :node {:op :local :local local})
-                  write (assoc value :node {:op :write :target (:target pair) :value read})]
-              [supply (conj bindings {:local local :init value}) (conj writes write)])) [supply [] []] pairs))
-
-(np/defpass parallel-node
-  {:from [l/Sequential Node] :to (t/HVec [l/Bound-Supply l/Assigned-Node]) :args [l/Bound-Supply]}
-  [supply node]
-  :literal [supply node]
-  :local [supply node]
-  :global [supply node]
-  :var [supply node]
-  :label [supply node]
-  :goto [supply node]
-  :native [supply node]
-  :dispatch (let [[supply value] (parallel supply (:value node))]
-              [supply {:op :dispatch :value value :spec (:spec node)}])
-  :case (let [[supply selector] (parallel supply (:selector node))
-              [supply arms] (parallel-forms supply (:arms node))]
-          [supply {:op :case :selector selector :arms arms}])
-  :recur (let [[supply args] (parallel-forms supply (:args node))]
-           [supply {:op :recur :target (:target node) :args args}])
-  :loop (let [[supply bindings] (parallel-bindings supply (:bindings node))
-              [supply body] (parallel supply (:body node))]
-          [supply {:op :loop :id (:id node) :bindings bindings :body body}])
-  :if (let [[supply test] (parallel supply (:test node))
-            [supply yes] (parallel supply (:then node))
-            [supply no] (parallel supply (:else node))]
-        [supply {:op :if :test test :then yes :else no}])
-  :throw (let [[supply value] (parallel supply (:value node))] [supply {:op :throw :value value}])
-  :write (let [[supply value] (parallel supply (:value node))]
-           [supply {:op :write :target (:target node) :value value}])
-  :do (let [[supply forms] (parallel-forms supply (:forms node))] [supply {:op :do :forms forms}])
-  :host (let [[supply args] (parallel-forms supply (:args node))]
-          [supply {:op :host :operation (:operation node) :args args}])
-  :invoke (let [[supply function] (parallel supply (:function node))
-                [supply args] (parallel-forms supply (:args node))]
-            [supply {:op :invoke :function function :args args}])
-  :collection (let [[supply items] (parallel-forms supply (:items node))
-                    [supply metadata] (if-let [metadata (:metadata node)] (parallel supply metadata) [supply nil])]
-                [supply {:op :collection :kind (:kind node) :items items :metadata metadata}])
-  :let (let [[supply bindings] (parallel-bindings supply (:bindings node))
-             [supply body] (parallel supply (:body node))]
-         [supply {:op :let :bindings bindings :body body}])
-  :psetq (let [[supply bindings writes] (save-parallel-values supply (:pairs node))
-               template (first writes)]
-           (if template
-             [supply {:op :let :bindings bindings
-                      :body (assoc template :node {:op :do :forms (conj writes (assoc template :node {:op :literal :value nil}))})}]
-             [supply {:op :literal :value nil}])))
-
-(np/defpass parallel
-  {:from [l/Sequential Expr] :to (t/HVec [l/Bound-Supply l/Assigned-Expr])
-   :args [l/Bound-Supply] :body true}
-  [supply expr]
-  (let [[supply node] (parallel-node supply (:node expr))] [supply (assoc expr :node node)]))
diff --git a/src/com/tailrecursion/blacktide/compiler/backend.clj b/src/com/tailrecursion/blacktide/compiler/backend.clj
deleted file mode 100644
index 279f53a..0000000
--- a/src/com/tailrecursion/blacktide/compiler/backend.clj
+++ /dev/null
@@ -1,26 +0,0 @@
-(ns ^:typed.clojure com.tailrecursion.blacktide.compiler.backend
-  "Select checked structured code when eligible, otherwise the general machine.
-  Independent models and both emitters exercise the same source semantics."
-  (:require [com.tailrecursion.nanopass :as np]
-            [com.tailrecursion.blacktide.compiler.languages :as l]
-            [com.tailrecursion.blacktide.compiler.feasible :as feasible]
-            [com.tailrecursion.blacktide.compiler.graph :as graph]
-            [com.tailrecursion.blacktide.compiler.invariants :as invariants]
-            [com.tailrecursion.blacktide.compiler.value-facts :as facts]
-            [com.tailrecursion.blacktide.compiler.joins :as joins]
-            [com.tailrecursion.blacktide.compiler.storage :as storage]
-            [com.tailrecursion.blacktide.compiler.compact :as compact]
-            [com.tailrecursion.blacktide.compiler.structure :as structure]
-            [com.tailrecursion.blacktide.compiler.structured-emit :as structured]
-            [com.tailrecursion.blacktide.compiler.emit :as emit]))
-
-(np/defpass compile-program {:from [l/Machine Program] :to [l/Machine Code] :body true}
-  [machine]
-  (let [program (-> {:entry (:entry machine) :blocks (:blocks machine)}
-                    feasible/program graph/analyze)
-        analysis (facts/analyze program)
-        decision (structure/recover (joins/plan analysis))]
-    (if (= :structured (:op decision))
-      (let [program (compact/program (storage/select-storage analysis (invariants/expression #{} (:body decision))))]
-        (structured/expression (:types program) (:body program)))
-      (emit/program-form machine))))
diff --git a/src/com/tailrecursion/blacktide/compiler/boundaries.clj b/src/com/tailrecursion/blacktide/compiler/boundaries.clj
deleted file mode 100644
index d7e1d5d..0000000
--- a/src/com/tailrecursion/blacktide/compiler/boundaries.clj
+++ /dev/null
@@ -1,70 +0,0 @@
-(ns ^:typed.clojure com.tailrecursion.blacktide.compiler.boundaries
-  "Classify analyzed native bodies before expression lowering. Native subtrees
-  exclude DSL effects; lowered loops materialize their source representations."
-  (:require [com.tailrecursion.blacktide.compiler.effects :refer [effects]]
-            [com.tailrecursion.nanopass :as np]
-            [com.tailrecursion.blacktide.compiler.source-languages :as s]
-            [com.tailrecursion.blacktide.compiler.loop-facts :as loops]
-            [typed.clojure :as-alias t]))
-
-(declare native native-binding native-forms classify classify-binding classify-forms classify-pair classify-pairs)
-
-(np/defpass native-node
-  {:from [s/Bound Node] :to [s/Native Node]
-   :processors {Expr native Exprs native-forms Binding native-binding}}
-  [node]
-  :literal node :local node :global node :var node
-  :case {:op :case :selector (native (:selector node))
-         :arms (native-forms (:arms node))}
-  :dispatch {:op :dispatch :value (native (:value node)) :spec (:spec node)}
-  :collection {:op :collection :kind (:kind node) :items (native-forms (:items node))
-               :metadata (when-let [metadata (:metadata node)] (native metadata))}
-  :recur {:op :recur :target (:target node) :args (mapv native (:args node))}
-  :loop {:op :loop :id (:id node) :bindings (mapv native-binding (:bindings node))
-         :body (native (:body node))}
-  :setq (if (empty? (:pairs node)) {:op :literal :value nil} (throw (IllegalStateException. "Native local write")))
-  :psetq (if (empty? (:pairs node)) {:op :literal :value nil} (throw (IllegalStateException. "Native parallel write")))
-  :label (throw (IllegalStateException. "Native label"))
-  :goto (throw (IllegalStateException. "Native transfer")))
-
-(np/defpass native-binding {:from [s/Bound Binding] :to [s/Native Binding] :body true}
-  [binding] (assoc binding :init (native (:init binding))))
-(np/defpass native-forms {:from [s/Bound Exprs] :to [s/Native Exprs] :processors {Expr native}}
-  [forms])
-(np/defpass native {:from [s/Bound Expr] :to [s/Native Expr] :body true}
-  [expr]
-  (assoc expr :node (native-node (:node expr))))
-
-(np/defpass classify-node
-  {:from [s/Bound Node] :to [s/Classified Node]
-   :processors {Expr classify Exprs classify-forms Binding classify-binding Assignments classify-pairs
-                Local clojure.core/identity RecurTarget clojure.core/identity
-                Raw clojure.core/identity CollectionKind clojure.core/identity CaseSpec clojure.core/identity}}
-  [node]
-  :loop {:op :loop :id (:id node) :bindings (mapv classify-binding (:bindings node)) :body (classify (:body node))})
-
-(np/defpass classify-pair {:from [s/Bound Assignment] :to [s/Classified Assignment]
-                          :processors {Expr classify Local clojure.core/identity}}
-  [pair])
-(np/defpass classify-pairs {:from [s/Bound Assignments] :to [s/Classified Assignments] :processors {Assignment classify-pair}}
-  [pairs])
-(np/defpass classify-binding {:from [s/Bound Binding] :to [s/Classified Binding]
-                             :processors {Expr classify Local clojure.core/identity}}
-  [binding])
-(np/defpass classify-forms {:from [s/Bound Exprs] :to [s/Classified Exprs] :processors {Expr classify}}
-  [forms])
-(np/defpass classify {:from [s/Bound Expr] :to [s/Classified Expr] :body true}
-  [expr]
-  (let [node (:node expr)]
-    (cond
-      ;; KeywordInvokeExpr snapshots its cache before evaluating the target.
-      ;; Retain the whole call when the target needs no DSL control or writes;
-      ;; saving the target first would change reentrant lookup behavior.
-      (and (= :invoke (:op node)) (= 1 (count (:args node)))
-           (= :literal (:op (:node (:function node))))
-           (keyword? (:form (:site (:function node)))) (not (effects expr)))
-      (assoc expr :node {:op :native :value (native expr)})
-      (and (= :loop (:op node)) (not (effects expr)))
-      (assoc expr :node {:op :native :value (native expr)})
-      (= :loop (:op node)) (let [expr (loops/lower expr)] (assoc expr :node (classify-node (:node expr))))
-      :else (assoc expr :node (classify-node node)))))
diff --git a/src/com/tailrecursion/blacktide/compiler/cases.clj b/src/com/tailrecursion/blacktide/compiler/cases.clj
deleted file mode 100644
index 422d17e..0000000
--- a/src/com/tailrecursion/blacktide/compiler/cases.clj
+++ /dev/null
@@ -1,24 +0,0 @@
-(ns ^:typed.clojure com.tailrecursion.blacktide.compiler.cases
-  "Separate source case from ordinal arm selection after native constant dispatch.
-  Each source arm occurs once, including grouped and hash-collision alternatives."
-  (:require [com.tailrecursion.nanopass :as np]
-            [com.tailrecursion.blacktide.compiler.source-languages :as s]))
-
-(declare normalize normalize-binding normalize-forms)
-
-(np/defpass normalize-node
-  {:from [s/Assigned Node] :to [s/Cases Node]
-   :processors {Expr normalize Exprs normalize-forms Binding normalize-binding
-                Raw clojure.core/identity Local clojure.core/identity CollectionKind clojure.core/identity
-                RecurTarget clojure.core/identity CaseSpec clojure.core/identity}}
-  [node]
-  :case
-  {:op :select :selector (normalize (:selector node)) :arms (normalize-forms (:arms node))})
-
-(np/defpass normalize-binding {:from [s/Assigned Binding] :to [s/Cases Binding]
-                              :processors {Expr normalize Local clojure.core/identity}}
-  [binding])
-(np/defpass normalize-forms {:from [s/Assigned Exprs] :to [s/Cases Exprs] :processors {Expr normalize}}
-  [forms])
-(np/defpass normalize {:from [s/Assigned Expr] :to [s/Cases Expr] :body true}
-  [expr] (assoc expr :node (normalize-node (:node expr))))
diff --git a/src/com/tailrecursion/blacktide/compiler/collections.clj b/src/com/tailrecursion/blacktide/compiler/collections.clj
deleted file mode 100644
index 02bd5d0..0000000
--- a/src/com/tailrecursion/blacktide/compiler/collections.clj
+++ /dev/null
@@ -1,71 +0,0 @@
-(ns ^:typed.clojure com.tailrecursion.blacktide.compiler.collections
-  "Preserve native constant collections and construction checks. Metadata keeps
-  construction dynamic and is evaluated only after that construction succeeds."
-  (:require [com.tailrecursion.nanopass :as np]
-            [com.tailrecursion.blacktide.compiler.source-languages :as l]
-            [com.tailrecursion.blacktide.compiler.languages :as m]
-            [com.tailrecursion.blacktide.compiler.host-emit :as host]
-            [typed.clojure :as-alias t]))
-
-(declare normalize normalize-binding)
-
-(np/defpass form
-  {:from [l/Collections CollectionKind] :to [m/Machine Code]
-   :args [(t/Vec m/Machine-Code) t/Bool] :body true}
-  [items unique? kind]
-  (if (= :vector kind)
-    (if (<= (count items) 6)
-      (list* 'clojure.lang.Tuple/create items)
-      ;; An Object expression prevents constant folding without unboxing the
-      ;; first literal, which would lose its original numeric width or identity.
-      (list 'do (assoc items 0 (host/box-form (nth items 0)))))
-    (list (if (= :set kind) 'clojure.lang.RT/set
-              (if unique? 'clojure.lang.RT/mapUniqueKeys 'clojure.lang.RT/map))
-          (list 'clojure.lang.RT/object_array items))))
-
-(np/defpass normalize-forms
-  {:from [l/Cases Exprs] :to [l/Collections Exprs] :processors {Expr normalize}}
-  [forms])
-
-(np/defpass literal-values
-  {:from [l/Collections Exprs] :to (t/Option (t/Vec t/Any)) :body true}
-  [items]
-  (reduce (fn [^{::t/- (t/Option (t/Vec t/Any))} values ^{::t/- l/Collections-Expr} expr]
-            (let [node (:node expr)]
-              (if (and values (= :literal (:op node))) (conj values (:value node)) (reduced nil))))
-          [] items))
-
-(np/defpass constant
-  {:from [l/Collections CollectionKind] :to t/Any :args [(t/Vec t/Any)] :body true}
-  [values kind]
-  (case kind
-    :vector (vec values)
-    :map (reduce (fn [^{::t/- (t/Map t/Any t/Any)} out ^{::t/- (t/Seq t/Any)} pair]
-                   (assoc out (first pair) (second pair))) {} (partition 2 values))
-    :set (set values)))
-
-(np/defpass normalize-node
-  {:from [l/Cases Node] :to [l/Collections Node]
-   :processors {Expr normalize Exprs normalize-forms Binding normalize-binding Raw clojure.core/identity
-                Local clojure.core/identity RecurTarget clojure.core/identity CaseSpec clojure.core/identity}}
-  [node]
-  :collection
-  (let [items (mapv normalize (:items node)) metadata (some-> (:metadata node) normalize)
-        values (literal-values items)
-        keys (when (= :map (:kind node)) (literal-values (vec (take-nth 2 items))))
-        unique? (boolean (and keys (= (count keys) (count (set keys)))))]
-    (when (and (nil? metadata) keys (not unique?))
-      (throw (IllegalArgumentException. "Duplicate constant keys in map")))
-    (if (and (nil? metadata) values)
-      {:op :literal :value (constant values (:kind node))}
-      {:op :build :kind (:kind node) :items items :metadata metadata :unique? unique?})))
-
-(np/defpass normalize-binding
-  {:from [l/Cases Binding] :to [l/Collections Binding]
-   :processors {Expr normalize Local clojure.core/identity}}
-  [binding])
-
-(np/defpass normalize
-  {:from [l/Cases Expr] :to [l/Collections Expr] :body true}
-  [expr]
-  (assoc expr :node (normalize-node (:node expr))))
diff --git a/src/com/tailrecursion/blacktide/compiler/compact_languages.clj b/src/com/tailrecursion/blacktide/compiler/compact_languages.clj
deleted file mode 100644
index aa881cd..0000000
--- a/src/com/tailrecursion/blacktide/compiler/compact_languages.clj
+++ /dev/null
@@ -1,24 +0,0 @@
-(ns com.tailrecursion.blacktide.compiler.compact-languages
-  "Lexical expressions after storage selection. Discarded results have an effect
-  destination; total primitive computations may occupy operand positions.
-  Native results may remain in tail position. Loop initializers and recur
-  arguments are explicit, separate from binders."
-  (:require [com.tailrecursion.nanopass :as np]
-            [com.tailrecursion.blacktide.compiler.languages :as l]
-            [typed.clojure :as-alias t]))
-
-(np/derive-language Compact l/Machine
-  {:add {Atom [(t/HMap :mandatory {:op ':inline :value Rhs :type (t/Option java.lang.Class)} :complete? true)]}
-   :replace
-   {Binding (t/HMap :mandatory {:target t/Sym :value Atom} :complete? true)
-    Expr (t/U (t/HMap :mandatory {:op ':bind :binding Operation :body Expr} :complete? true)
-              (t/HMap :mandatory {:op ':effect :binding Operation :body Expr} :complete? true)
-              (t/HMap :mandatory {:op ':join :target t/Sym :value Expr :body Expr} :complete? true)
-              (t/HMap :mandatory {:op ':branch :test Atom :yes Expr :no Expr} :complete? true)
-              (t/HMap :mandatory {:op ':switch :test Atom :arms (t/Vec Expr)} :complete? true)
-              (t/HMap :mandatory {:op ':loop :bindings (t/Vec Binding) :body Expr} :complete? true)
-              (t/HMap :mandatory {:op ':recur :args (t/Vec Atom)} :complete? true)
-              (t/HMap :mandatory {:op ':return :value Atom} :complete? true)
-              (t/HMap :mandatory {:op ':throw :value Atom} :complete? true))
-    Types (t/Map t/Sym java.lang.Class)
-    Program (t/HMap :mandatory {:body Expr :types Types} :complete? true)}})
diff --git a/src/com/tailrecursion/blacktide/compiler/effects.clj b/src/com/tailrecursion/blacktide/compiler/effects.clj
deleted file mode 100644
index 8562b1b..0000000
--- a/src/com/tailrecursion/blacktide/compiler/effects.clj
+++ /dev/null
@@ -1,28 +0,0 @@
-(ns ^:typed.clojure com.tailrecursion.blacktide.compiler.effects
-  "Identify local writes and transfers that prevent native subtree emission."
-  (:require [com.tailrecursion.nanopass :as np]
-            [com.tailrecursion.blacktide.compiler.source-languages :as s]
-            [typed.clojure :as-alias t]))
-
-(declare effects)
-
-(np/defpass effect-node {:from [s/Bound Node] :to t/Bool}
-  [node]
-  :literal false :local false :global false :var false
-  :setq (boolean (seq (:pairs node)))
-  :psetq (boolean (seq (:pairs node)))
-  :label true :goto true
-  :if (or (effects (:test node)) (effects (:then node)) (effects (:else node)))
-  :case (or (effects (:selector node)) (boolean (some effects (:arms node))))
-  :dispatch (effects (:value node))
-  :throw (effects (:value node))
-  :host (boolean (some effects (:args node)))
-  :invoke (or (effects (:function node)) (boolean (some effects (:args node))))
-  :collection (or (boolean (some effects (:items node))) (boolean (some-> (:metadata node) effects)))
-  :do (boolean (some effects (:forms node)))
-  :let (or (boolean (some #(effects (:init %)) (:bindings node))) (effects (:body node)))
-  :loop (or (boolean (some #(effects (:init %)) (:bindings node))) (effects (:body node)))
-  :recur (boolean (some effects (:args node))))
-
-(np/defpass effects {:from [s/Bound Expr] :to t/Bool :body true}
-  [expr] (effect-node (:node expr)))
diff --git a/src/com/tailrecursion/blacktide/compiler/errors.clj b/src/com/tailrecursion/blacktide/compiler/errors.clj
index caef819..b07e727 100644
--- a/src/com/tailrecursion/blacktide/compiler/errors.clj
+++ b/src/com/tailrecursion/blacktide/compiler/errors.clj
@@ -2,24 +2,24 @@
   "Source locations and structured errors shared by syntax analysis and lowering.
   DSL markers obey the same Var visibility rules as other source forms."
   (:require [com.tailrecursion.nanopass :as np]
-            [com.tailrecursion.blacktide.compiler.source-languages :as s]
+            [com.tailrecursion.blacktide.compiler.languages :as l]
             [typed.clojure :as-alias t]))
 
-(np/defpass site {:from [s/Bound Raw] :to [s/Bound Site] :args [s/Bound-Site] :body true}
+(np/defpass site {:from [l/Bound Raw] :to [l/Bound Site] :args [l/Bound-Site] :body true}
   [parent form]
-  ((fn ^{::t/- [t/Any t/Any t/Any :-> s/Bound-Site]} location [file line column]
+  ((fn ^{::t/- [t/Any t/Any t/Any :-> l/Bound-Site]} location [file line column]
      {:form form :file (if (string? file) file (:file parent))
       :line (if (integer? line) line (:line parent))
       :column (if (integer? column) column (:column parent))})
    (:file (meta form)) (:line (meta form)) (:column (meta form))))
 
 (np/defpass fail
-  {:from [s/Bound Site] :to t/Nothing :args [t/Kw t/Str (t/Map t/Kw t/Any)] :body true}
+  {:from [l/Bound Site] :to t/Nothing :args [t/Kw t/Str (t/Map t/Kw t/Any)] :body true}
   [kind message details site]
   (throw (ex-info message (merge site details {:kind kind}))))
 
 (np/defpass check-visibility
-  {:from [s/Bound Site] :to nil :args [t/AnyVar] :body true}
+  {:from [l/Bound Site] :to nil :args [t/AnyVar] :body true}
   [v site]
   ;; The public Var field is authoritative; :ns metadata can be changed.
   (when (and (not (.isPublic ^clojure.lang.Var v))
diff --git a/src/com/tailrecursion/blacktide/compiler/jvm.clj b/src/com/tailrecursion/blacktide/compiler/jvm.clj
index 14614fc..ef77400 100644
--- a/src/com/tailrecursion/blacktide/compiler/jvm.clj
+++ b/src/com/tailrecursion/blacktide/compiler/jvm.clj
@@ -3,7 +3,7 @@
   analyzer. Method inference follows the pinned Clojure overload rules."
   (:refer-clojure :exclude [methods])
   (:require [com.tailrecursion.nanopass :as np]
-            [com.tailrecursion.blacktide.compiler.jvm-languages :as l]
+            [com.tailrecursion.blacktide.compiler.languages :as l]
             [typed.clojure :as-alias t]))
 
 (np/defpass resolved-class
diff --git a/src/com/tailrecursion/blacktide/compiler/jvm_languages.clj b/src/com/tailrecursion/blacktide/compiler/jvm_languages.clj
deleted file mode 100644
index 7a04a91..0000000
--- a/src/com/tailrecursion/blacktide/compiler/jvm_languages.clj
+++ /dev/null
@@ -1,35 +0,0 @@
-(ns com.tailrecursion.blacktide.compiler.jvm-languages
-  "JVM facts and host operation descriptors contain no executable source children.
-  Unknown type, known nil and a known JVM class are distinct facts."
-  (:require [com.tailrecursion.nanopass :as np]
-            [typed.clojure :as-alias t]))
-
-(np/deflanguage JVM
-  {Raw t/Any
-   Type (t/U ':unknown nil java.lang.Class)
-   Seed (t/HMap :mandatory {:hint Raw :var-meta (t/Map t/Any t/Any)} :complete? true)
-   Fact (t/HMap :mandatory {:hint Raw :var-meta (t/Map t/Any t/Any)
-                            :type Type :primitive? t/Bool :hint-error (t/Option t/Str)} :complete? true)
-   Types (t/Vec Type)
-   Tags (t/Option (t/Vec Raw))
-   Mode (t/U ':method ':member ':field)
-   Protocol (t/HMap :mandatory {:class (t/Option java.lang.Class) :member (t/Option t/Sym)
-                                 :parameters (t/Vec java.lang.Class)} :complete? true)
-   Operation (t/U (t/HMap :mandatory {:op ':keyword-site :key t/Kw} :complete? true)
-                  (t/HMap :mandatory {:op ':protocol :method Protocol} :complete? true)
-                  (t/HMap :mandatory {:op ':direct :class java.lang.Class :parameters (t/Vec java.lang.Class)
-                                                    :variadic? t/Bool :return java.lang.Class} :complete? true)
-                  (t/HMap :mandatory {:op ':instanceof :class java.lang.Class} :complete? true)
-                  (t/HMap :mandatory {:op ':adapt :class java.lang.Class} :complete? true)
-                  (t/HMap :mandatory {:op ':constructor :class java.lang.Class :tags Tags} :complete? true)
-                  (t/HMap :mandatory {:op ':static :class java.lang.Class :member t/Sym :tags Tags} :complete? true)
-                  (t/HMap :mandatory {:op ':instance :class (t/Option java.lang.Class) :member t/Sym
-                                      :mode Mode :tags Tags} :complete? true)
-                  (t/HMap :mandatory {:op ':static-field :class java.lang.Class :member t/Sym} :complete? true)
-                  (t/HMap :mandatory {:op ':method-value :name t/Sym} :complete? true))
-   Syntax (t/HMap :mandatory {:operation Operation :operands (t/Vec Raw)} :complete? true)
-   Method (t/U java.lang.reflect.Method java.lang.reflect.Constructor)
-   Methods (t/Vec Method)
-   Conversion (t/HMap :mandatory {:type java.lang.Class :parameter? t/Bool} :complete? true)
-   Plan (t/HMap :mandatory {:operation Operation :conversions (t/Vec (t/Option Conversion))} :complete? true)
-   Selection (t/HMap :mandatory {:method (t/Option Method) :exact? t/Bool :tied? t/Bool} :complete? true)})
diff --git a/src/com/tailrecursion/blacktide/compiler/languages.clj b/src/com/tailrecursion/blacktide/compiler/languages.clj
index 92f9341..75fe9fb 100644
--- a/src/com/tailrecursion/blacktide/compiler/languages.clj
+++ b/src/com/tailrecursion/blacktide/compiler/languages.clj
@@ -1,24 +1,147 @@
 (ns com.tailrecursion.blacktide.compiler.languages
-  "Loophole's backend languages. Stable operands are separate from evaluations;
-  allocated machines explicitly name the values live at every block entry."
+  "All compiler languages, in dependency order: JVM facts, source trees, graphs,
+  analyses and lexical control. Local nonterminals rebind during derivation;
+  qualified l/* types refer to another language here. Execution order lives in
+  com.tailrecursion.blacktide.compiler."
   (:require [com.tailrecursion.nanopass :as np]
-            [com.tailrecursion.blacktide.compiler.source-languages :as s]
-            [typed.clojure :as-alias t]
-            [com.tailrecursion.blacktide.compiler.jvm-languages :as j]))
+            [com.tailrecursion.blacktide.compiler.languages :as-alias l]
+            [typed.clojure :as-alias t]))
 
+;; Shared JVM facts and operations.
+(np/deflanguage JVM
+  {Raw t/Any
+   Type (t/U ':unknown nil java.lang.Class)
+   Seed (t/HMap :mandatory {:hint Raw :var-meta (t/Map t/Any t/Any)} :complete? true)
+   Fact (t/HMap :mandatory {:hint Raw :var-meta (t/Map t/Any t/Any)
+                            :type Type :primitive? t/Bool :hint-error (t/Option t/Str)} :complete? true)
+   Types (t/Vec Type)
+   Tags (t/Option (t/Vec Raw))
+   Mode (t/U ':method ':member ':field)
+   Protocol (t/HMap :mandatory {:class (t/Option java.lang.Class) :member (t/Option t/Sym)
+                                 :parameters (t/Vec java.lang.Class)} :complete? true)
+   Operation (t/U (t/HMap :mandatory {:op ':keyword-site :key t/Kw} :complete? true)
+                  (t/HMap :mandatory {:op ':protocol :method Protocol} :complete? true)
+                  (t/HMap :mandatory {:op ':direct :class java.lang.Class :parameters (t/Vec java.lang.Class)
+                                                    :variadic? t/Bool :return java.lang.Class} :complete? true)
+                  (t/HMap :mandatory {:op ':instanceof :class java.lang.Class} :complete? true)
+                  (t/HMap :mandatory {:op ':adapt :class java.lang.Class} :complete? true)
+                  (t/HMap :mandatory {:op ':constructor :class java.lang.Class :tags Tags} :complete? true)
+                  (t/HMap :mandatory {:op ':static :class java.lang.Class :member t/Sym :tags Tags} :complete? true)
+                  (t/HMap :mandatory {:op ':instance :class (t/Option java.lang.Class) :member t/Sym
+                                      :mode Mode :tags Tags} :complete? true)
+                  (t/HMap :mandatory {:op ':static-field :class java.lang.Class :member t/Sym} :complete? true)
+                  (t/HMap :mandatory {:op ':method-value :name t/Sym} :complete? true))
+   Syntax (t/HMap :mandatory {:operation Operation :operands (t/Vec Raw)} :complete? true)
+   Method (t/U java.lang.reflect.Method java.lang.reflect.Constructor)
+   Methods (t/Vec Method)
+   Conversion (t/HMap :mandatory {:type java.lang.Class :parameter? t/Bool} :complete? true)
+   Plan (t/HMap :mandatory {:operation Operation :conversions (t/Vec (t/Option Conversion))} :complete? true)
+   Selection (t/HMap :mandatory {:method (t/Option Method) :exact? t/Bool :tied? t/Bool} :complete? true)})
+
+;; Source normalization: Bound -> Classified -> Sequential -> Assigned ->
+;; Cases -> Collections -> Prepared -> Ordered. Native is a restricted subtree.
+(np/deflanguage Bound
+  {Raw t/Any
+   Forms (t/Vec Raw)
+   Site (t/HMap :mandatory {:form Raw :file (t/Option t/Str)
+                            :line (t/Option t/Int) :column (t/Option t/Int)} :complete? true)
+   Jvm l/JVM-Fact
+   Local (t/HMap :mandatory {:id t/Sym :name t/Sym :mutable? t/Bool :external? t/Bool
+                             :jvm Jvm} :complete? true)
+   Binding (t/HMap :mandatory {:local Local :init Expr} :complete? true)
+   Bindings (t/Vec Binding)
+   Assignment (t/HMap :mandatory {:target Local :value Expr} :complete? true)
+   Assignments (t/Vec Assignment)
+   RecurTarget (t/HMap :mandatory {:id t/Sym :bindings (t/Vec Local)} :complete? true)
+   CaseSpec (t/HMap :mandatory {:shift t/Int :mask t/Int :default t/Int
+                                :tests (t/Map t/Int (t/HVec [Raw t/Int]))
+                                :switch (t/U ':compact ':sparse)
+                                :test (t/U ':int ':hash-equiv ':hash-identity)
+                                :skip (t/Option (t/Set t/Int))} :complete? true)
+   CaseSyntax (t/HMap :mandatory {:selector Raw :arms Forms} :complete? true)
+   DispatchSyntax (t/HMap :mandatory {:value Raw :spec CaseSpec :arms Forms} :complete? true)
+   Exprs (t/Vec Expr)
+   CollectionKind (t/U ':vector ':map ':set)
+   Expr (t/HMap :mandatory {:site Site :jvm Jvm :node Node} :complete? true)
+   Node (t/U (t/HMap :mandatory {:op ':literal :value Raw} :complete? true)
+             (t/HMap :mandatory {:op ':local :local Local} :complete? true)
+             (t/HMap :mandatory {:op ':global :name t/Sym} :complete? true)
+             (t/HMap :mandatory {:op ':var :name t/Sym} :complete? true)
+             (t/HMap :mandatory {:op ':host :operation l/JVM-Operation :args (t/Vec Expr)} :complete? true)
+             (t/HMap :mandatory {:op ':invoke :function Expr :args (t/Vec Expr)} :complete? true)
+             (t/HMap :mandatory {:op ':collection :kind CollectionKind :items Exprs
+                                 :metadata (t/Option Expr)} :complete? true)
+             (t/HMap :mandatory {:op ':setq :pairs Assignments} :complete? true)
+             (t/HMap :mandatory {:op ':psetq :pairs Assignments} :complete? true)
+             (t/HMap :mandatory {:op ':if :test Expr :then Expr :else Expr} :complete? true)
+             (t/HMap :mandatory {:op ':case :selector Expr :arms Exprs} :complete? true)
+             (t/HMap :mandatory {:op ':dispatch :value Expr :spec CaseSpec} :complete? true)
+             (t/HMap :mandatory {:op ':throw :value Expr} :complete? true)
+             (t/HMap :mandatory {:op ':label :name t/Sym} :complete? true)
+             (t/HMap :mandatory {:op ':goto :name t/Sym} :complete? true)
+             (t/HMap :mandatory {:op ':do :forms (t/Vec Expr)} :complete? true)
+             (t/HMap :mandatory {:op ':let :bindings (t/Vec Binding) :body Expr} :complete? true)
+             (t/HMap :mandatory {:op ':loop :id t/Sym :bindings (t/Vec Binding) :body Expr} :complete? true)
+             (t/HMap :mandatory {:op ':recur :target RecurTarget :args (t/Vec Expr)} :complete? true))
+   Supply (t/HMap :mandatory {:prefix t/Str :next t/Int} :complete? true)
+   Context (t/HMap :mandatory {:locals (t/Map t/Sym Local) :macro-env (t/Map t/Sym Raw)
+                               :markers (t/Map t/AnyVar t/Kw) :outer? t/Bool :transfer? t/Bool
+                               :site Site
+                               :recur (t/Option RecurTarget) :statement? t/Bool :tail? t/Bool} :complete? true)})
+
+(np/derive-language Native Bound
+  {:remove {Node #{:setq :psetq :label :goto}}})
+
+(np/derive-language Classified Bound
+  {:add {Node [(t/HMap :mandatory {:op ':native :value l/Native-Expr}
+                       :complete? true)]}})
+
+(np/derive-language Sequential Classified
+  {:remove {Node #{:setq}}
+   :add {Node [(t/HMap :mandatory {:op ':write :target Local :value Expr} :complete? true)]}})
+
+(np/derive-language Assigned Sequential
+  {:remove {Node #{:psetq}}})
+
+(np/derive-language Cases Assigned
+  {:remove {Node #{:case}}
+   :add {Node [(t/HMap :mandatory {:op ':select :selector Expr :arms Exprs} :complete? true)]}})
+
+(np/derive-language Collections Cases
+  {:remove {Node #{:collection}}
+   :add {Node [(t/HMap :mandatory {:op ':build :kind CollectionKind :items Exprs :unique? t/Bool
+                                  :metadata (t/Option Expr)} :complete? true)]}})
+
+(np/derive-language Prepared Collections
+  {:remove {Node #{:host}}
+   :add {Node [(t/HMap :mandatory {:op ':host-call :operation l/JVM-Operation :args (t/Vec Expr)} :complete? true)
+                (t/HMap :mandatory {:op ':convert :conversion l/JVM-Conversion :value Expr} :complete? true)]}})
+
+(np/derive-language Ordered Prepared
+  {:remove {Node #{:invoke :build :host-call :convert :recur :dispatch}}
+   :add {Node [(t/HMap :mandatory {:op ':convert :conversion l/JVM-Conversion :value Local} :complete? true)
+                (t/HMap :mandatory {:op ':dispatch :value Local :spec CaseSpec} :complete? true)
+                (t/HMap :mandatory {:op ':recur :target RecurTarget :args (t/Vec Local)} :complete? true)
+                (t/HMap :mandatory {:op ':host-call :operation l/JVM-Operation :args (t/Vec Local)} :complete? true)
+                (t/HMap :mandatory {:op ':invoke :function Local :args (t/Vec Local)} :complete? true)
+                (t/HMap :mandatory {:op ':build :kind CollectionKind :items (t/Vec Local) :unique? t/Bool} :complete? true)
+                (t/HMap :mandatory {:op ':metadata :value Local :metadata Local} :complete? true)]}})
+
+;; Graph execution order is Symbolic -> CFG -> Machine. Derivation runs
+;; the other way because earlier graphs extend the allocated machine grammar.
 (np/deflanguage Machine
   {Atom (t/U (t/HMap :mandatory {:op ':literal :value t/Any} :complete? true)
              (t/HMap :mandatory {:op ':local :id t/Sym} :complete? true))
-   Operand (t/HMap :mandatory {:value Atom :jvm j/JVM-Fact} :complete? true)
-   Rhs (t/U (t/HMap :mandatory {:op ':convert :conversion j/JVM-Conversion :value Operand} :complete? true)
-           (t/HMap :mandatory {:op ':dispatch :value Operand :spec s/Bound-CaseSpec} :complete? true)
-           (t/HMap :mandatory {:op ':native :value s/Native-Expr} :complete? true)
-           (t/HMap :mandatory {:op ':host :operation j/JVM-Operation :args (t/Vec Operand)} :complete? true)
+   Operand (t/HMap :mandatory {:value Atom :jvm l/JVM-Fact} :complete? true)
+   Rhs (t/U (t/HMap :mandatory {:op ':convert :conversion l/JVM-Conversion :value Operand} :complete? true)
+           (t/HMap :mandatory {:op ':dispatch :value Operand :spec l/Bound-CaseSpec} :complete? true)
+           (t/HMap :mandatory {:op ':native :value l/Native-Expr} :complete? true)
+           (t/HMap :mandatory {:op ':host :operation l/JVM-Operation :args (t/Vec Operand)} :complete? true)
             (t/HMap :mandatory {:op ':value :value Atom} :complete? true)
             (t/HMap :mandatory {:op ':global :name t/Sym} :complete? true)
-            (t/HMap :mandatory {:op ':external :name t/Sym :jvm j/JVM-Fact} :complete? true)
+            (t/HMap :mandatory {:op ':external :name t/Sym :jvm l/JVM-Fact} :complete? true)
             (t/HMap :mandatory {:op ':var :name t/Sym} :complete? true)
-            (t/HMap :mandatory {:op ':construct :kind s/Bound-CollectionKind :items (t/Vec Atom) :unique? t/Bool} :complete? true)
+            (t/HMap :mandatory {:op ':construct :kind l/Bound-CollectionKind :items (t/Vec Atom) :unique? t/Bool} :complete? true)
             (t/HMap :mandatory {:op ':metadata :value Atom :metadata Atom} :complete? true)
             (t/HMap :mandatory {:op ':invoke :function Atom :args (t/Vec Atom)} :complete? true))
    Operation (t/HMap :mandatory {:target t/Sym :value Rhs} :complete? true)
@@ -45,10 +168,73 @@
     Predecessors (t/Map Label Labels)}})
 
 (np/derive-language Symbolic CFG
-  {:add {Term [(t/HMap :mandatory {:op ':transfer :name t/Sym :site s/Bound-Site} :complete? true)]}
+  {:add {Term [(t/HMap :mandatory {:op ':transfer :name t/Sym :site l/Bound-Site} :complete? true)]}
    :replace
    {LabelTable (t/Map t/Sym Label)
     Program (t/HMap :mandatory {:entry Label :blocks (t/Map Label Block) :labels LabelTable} :complete? true)
-    Builder (t/HMap :mandatory {:supply s/Bound-Supply :ops (t/Vec Operation) :next Label :current Label
+    Builder (t/HMap :mandatory {:supply l/Bound-Supply :ops (t/Vec Operation) :next Label :current Label
                                 :blocks (t/Map Label Block) :labels LabelTable
                                 :loops (t/Map t/Sym Label)} :complete? true)}})
+
+;; Facts and plans used by structured control recovery.
+(np/deflanguage Analysis
+  {Type (t/U ':bottom ':boxed java.lang.Class)
+   Types (t/Map t/Sym Type)
+   Definitions (t/Map t/Sym (t/Vec l/Machine-Rhs))
+   Program (t/HMap :mandatory {:machine l/Machine-Program :types Types
+                               :reachable (t/Set l/Machine-Label)} :complete? true)})
+
+(np/derive-language Planned Analysis
+  {:replace
+   {Postdominators (t/Map l/Machine-Label (t/Set l/Machine-Label))
+    Region (t/HMap :mandatory {:definite (t/Set t/Sym) :changed (t/Set t/Sym)} :complete? true)
+    Regions (t/Map l/Machine-Label Region)
+    Join (t/HMap :mandatory {:target l/Machine-Label :result t/Sym} :complete? true)
+    Joins (t/Map l/Machine-Label Join)
+    Program (t/HMap :mandatory {:machine l/Machine-Program :types Types
+                                :reachable (t/Set l/Machine-Label) :joins Joins} :complete? true)}})
+
+;; Structured recovery produces Joined expressions, then Stored programs.
+(np/deflanguage Structured
+  {Expr (t/U (t/HMap :mandatory {:op ':bind :binding l/Machine-Operation :body Expr} :complete? true)
+             (t/HMap :mandatory {:op ':branch :test l/Machine-Atom :yes Expr :no Expr} :complete? true)
+             (t/HMap :mandatory {:op ':switch :test l/Machine-Atom :arms (t/Vec Expr)} :complete? true)
+             (t/HMap :mandatory {:op ':loop :slots (t/Vec t/Sym) :body Expr} :complete? true)
+             (t/HMap :mandatory {:op ':recur :slots (t/Vec t/Sym)} :complete? true)
+             (t/HMap :mandatory {:op ':return :value l/Machine-Atom} :complete? true)
+             (t/HMap :mandatory {:op ':throw :value l/Machine-Atom} :complete? true))
+   Build (t/U (t/HMap :mandatory {:op ':built :budget t/Int :blocks (t/Vec l/Machine-Label) :expr Expr} :complete? true)
+               (t/HMap :mandatory {:op ':exhausted} :complete? true))
+   Arms (t/U (t/HMap :mandatory {:op ':built :budget t/Int :blocks (t/Vec l/Machine-Label) :arms (t/Vec Expr)} :complete? true)
+              (t/HMap :mandatory {:op ':exhausted} :complete? true))
+   Scan (t/HVec [(t/Set l/Machine-Label) (t/Set l/Machine-Label)])
+   Fallback (t/U ':cycles ':validation ':budget ':native-sites)
+   Decision (t/U (t/HMap :mandatory {:op ':structured :body Expr} :complete? true)
+                 (t/HMap :mandatory {:op ':fallback :reason Fallback} :complete? true))})
+
+(np/derive-language Joined Structured
+  {:add {Expr [(t/HMap :mandatory {:op ':join :target t/Sym :value Expr :body Expr} :complete? true)]}
+   :replace {Context (t/HMap :mandatory {:machine l/Machine-Program :header (t/Option l/Machine-Label)
+                                        :slots (t/Vec t/Sym) :joins l/Planned-Joins
+                                        :stop (t/Option (t/HVec [l/Machine-Label t/Sym]))} :complete? true)}})
+
+(np/derive-language Stored Joined
+  {:replace {Types (t/Map t/Sym java.lang.Class)
+             Program (t/HMap :mandatory {:body Expr :types Types} :complete? true)}})
+
+;; Compact extends machine operands for lexical emission after storage.
+(np/derive-language Compact Machine
+  {:add {Atom [(t/HMap :mandatory {:op ':inline :value Rhs :type (t/Option java.lang.Class)} :complete? true)]}
+   :replace
+   {Binding (t/HMap :mandatory {:target t/Sym :value Atom} :complete? true)
+    Expr (t/U (t/HMap :mandatory {:op ':bind :binding Operation :body Expr} :complete? true)
+              (t/HMap :mandatory {:op ':effect :binding Operation :body Expr} :complete? true)
+              (t/HMap :mandatory {:op ':join :target t/Sym :value Expr :body Expr} :complete? true)
+              (t/HMap :mandatory {:op ':branch :test Atom :yes Expr :no Expr} :complete? true)
+              (t/HMap :mandatory {:op ':switch :test Atom :arms (t/Vec Expr)} :complete? true)
+              (t/HMap :mandatory {:op ':loop :bindings (t/Vec Binding) :body Expr} :complete? true)
+              (t/HMap :mandatory {:op ':recur :args (t/Vec Atom)} :complete? true)
+              (t/HMap :mandatory {:op ':return :value Atom} :complete? true)
+              (t/HMap :mandatory {:op ':throw :value Atom} :complete? true))
+    Types (t/Map t/Sym java.lang.Class)
+    Program (t/HMap :mandatory {:body Expr :types Types} :complete? true)}})
diff --git a/src/com/tailrecursion/blacktide/compiler/lower.clj b/src/com/tailrecursion/blacktide/compiler/lower.clj
deleted file mode 100644
index e889868..0000000
--- a/src/com/tailrecursion/blacktide/compiler/lower.clj
+++ /dev/null
@@ -1,141 +0,0 @@
-(ns ^:typed.clojure com.tailrecursion.blacktide.compiler.lower
-  "Lower ordered expressions into a CFG with explicit branch joins and exits.
-  Operands have explicit bindings; operations retain their execution position."
-  (:require [com.tailrecursion.blacktide.compiler.errors :as errors]
-            [com.tailrecursion.nanopass :as np]
-            [com.tailrecursion.blacktide.compiler.source-languages :as s]
-            [com.tailrecursion.blacktide.compiler.languages :as l]
-            [com.tailrecursion.blacktide.compiler.source :as source]
-            [typed.clojure :as-alias t]))
-
-(declare lower lower-forms lower-bindings)
-
-(np/defpass fresh-block
-  {:from [l/Symbolic Builder] :to (t/HVec [l/Symbolic-Builder l/Symbolic-Label]) :body true}
-  [builder]
-  [(assoc builder :next (inc (:next builder))) (:next builder)])
-
-(np/defpass start
-  {:from [l/Symbolic Label] :to [l/Symbolic Builder] :args [l/Symbolic-Builder] :body true}
-  [builder label]
-  (assoc builder :current label :ops []))
-
-(np/defpass seal
-  {:from [l/Symbolic Term] :to [l/Symbolic Builder] :args [l/Symbolic-Builder] :body true}
-  [builder term]
-  (if (contains? (:blocks builder) (:current builder))
-    (throw (ex-info "Control-flow block already has a terminator" {:kind :invalid-graph :block (:current builder)}))
-    (assoc builder :blocks (assoc (:blocks builder) (:current builder) {:ops (:ops builder) :term term}) :ops [])))
-
-(np/defpass evaluate
-  {:from [l/Symbolic Rhs] :to (t/HVec [l/Symbolic-Builder l/Symbolic-Atom])
-   :args [l/Symbolic-Builder] :body true}
-  [builder rhs]
-  (let [[supply id] (source/fresh "value" (:supply builder))]
-    [(-> builder (assoc :supply supply) (update :ops conj {:target id :value rhs})) {:op :local :id id}]))
-
-(np/defpass lower-forms
-  {:from [s/Ordered Exprs] :to (t/HVec [l/Symbolic-Builder (t/Vec l/Symbolic-Atom)])
-   :args [l/Symbolic-Builder] :body true}
-  [builder forms]
-  (reduce (fn [^{::t/- (t/HVec [l/Symbolic-Builder (t/Vec l/Symbolic-Atom)])} acc ^{::t/- s/Ordered-Expr} expr]
-            (let [[builder out] acc [builder value] (lower builder expr)] [builder (conj out value)]))
-          [builder []] forms))
-
-(np/defpass lower-bindings
-  {:from [s/Ordered Bindings] :to [l/Symbolic Builder] :args [l/Symbolic-Builder] :body true}
-  [builder bindings]
-  (reduce (fn [^{::t/- l/Symbolic-Builder} builder ^{::t/- s/Ordered-Binding} binding]
-            (let [[builder value] (lower builder (:init binding))]
-              (update builder :ops conj {:target (:id (:local binding)) :value {:op :value :value value}})))
-          builder bindings))
-
-(np/defpass lower-node
-  {:from [s/Ordered Node] :to (t/HVec [l/Symbolic-Builder l/Symbolic-Atom])
-   :args [l/Symbolic-Builder s/Ordered-Expr]}
-  [builder expr node]
-  :literal [builder {:op :literal :value (:value node)}]
-  :local (let [local (:local node)]
-           (evaluate builder (if (:external? local) {:op :external :name (:id local) :jvm (:jvm local)}
-                                 {:op :value :value {:op :local :id (:id local)}})))
-  :global (evaluate builder {:op :global :name (:name node)})
-  :var (evaluate builder {:op :var :name (:name node)})
-  :native (evaluate builder node)
-  :dispatch (evaluate builder {:op :dispatch :spec (:spec node)
-                               :value {:value {:op :local :id (:id (:value node))} :jvm (:jvm (:value node))}})
-  :select
-  (let [[builder test] (lower builder (:selector node))
-        [builder join] (fresh-block builder)
-        [supply result] (source/fresh "case_result" (:supply builder))
-        [builder targets] (reduce (fn [^{::t/- (t/HVec [l/Symbolic-Builder (t/Vec l/Symbolic-Label)])} acc
-                                       ^{::t/- s/Ordered-Expr} _]
-                                    (let [[builder targets] acc [builder label] (fresh-block builder)]
-                                      [builder (conj targets label)]))
-                                  [(assoc builder :supply supply) []] (:arms node))
-        builder (seal builder {:op :switch :test test :targets targets})
-        builder (reduce (fn [^{::t/- l/Symbolic-Builder} builder ^{::t/- t/Int} index]
-                          (let [[builder value] (lower (start builder (nth targets index)) (nth (:arms node) index))]
-                            (seal (update builder :ops conj {:target result :value {:op :value :value value}})
-                                  {:op :jump :target join}))) builder (range (count targets)))]
-    [(start builder join) {:op :local :id result}])
-  :loop (let [builder (lower-bindings builder (:bindings node))
-              [builder header] (fresh-block builder)
-              builder (assoc builder :loops (assoc (:loops builder) (:id node) header))]
-          (lower (start (seal builder {:op :jump :target header}) header) (:body node)))
-  :recur (let [target (:target node)
-               header (or (get (:loops builder) (:id target))
-                          (errors/fail :invalid-graph "Missing source recur target" {} (:site expr)))
-               writes (mapv (fn [local arg] {:target (:id local) :value {:op :value :value {:op :local :id (:id arg)}}})
-                            (:bindings target) (:args node))
-               builder (seal (update builder :ops into writes) {:op :jump :target header})
-               [builder next] (fresh-block builder)]
-           [(start builder next) {:op :literal :value nil}])
-  :if (let [[builder test] (lower builder (:test node))
-            [builder yes] (fresh-block builder) [builder no] (fresh-block builder) [builder join] (fresh-block builder)
-            [supply result] (source/fresh "join" (:supply builder))
-            builder (seal (assoc builder :supply supply) {:op :branch :test test :yes yes :no no})
-            [builder a] (lower (start builder yes) (:then node))
-            builder (seal (update builder :ops conj {:target result :value {:op :value :value a}}) {:op :jump :target join})
-            [builder b] (lower (start builder no) (:else node))
-            builder (seal (update builder :ops conj {:target result :value {:op :value :value b}}) {:op :jump :target join})]
-        [(start builder join) {:op :local :id result}])
-  :throw (let [[builder value] (lower builder (:value node))
-               builder (seal builder {:op :throw :value value})
-               [builder next] (fresh-block builder)]
-           [(start builder next) {:op :literal :value nil}])
-  :write (let [[builder value] (lower builder (:value node))]
-           [(update builder :ops conj {:target (:id (:target node)) :value {:op :value :value value}}) value])
-  :convert (evaluate builder {:op :convert :conversion (:conversion node)
-                              :value {:value {:op :local :id (:id (:value node))} :jvm (:jvm (:value node))}})
-  :host-call (evaluate builder {:op :host :operation (:operation node)
-                           :args (mapv (fn [local] {:value {:op :local :id (:id local)} :jvm (:jvm local)}) (:args node))})
-  :invoke (evaluate builder {:op :invoke :function {:op :local :id (:id (:function node))}
-                             :args (mapv (fn [local] {:op :local :id (:id local)}) (:args node))})
-  :build (evaluate builder {:op :construct :kind (:kind node) :unique? (:unique? node)
-                            :items (mapv (fn [local] {:op :local :id (:id local)}) (:items node))})
-  :metadata (evaluate builder {:op :metadata :value {:op :local :id (:id (:value node))}
-                               :metadata {:op :local :id (:id (:metadata node))}})
-  :label (let [key (:name node)]
-           (when (contains? (:labels builder) key)
-             (errors/fail :duplicate-label "Duplicate label" {:label (:name node)} (:site expr)))
-           (let [[builder label] (fresh-block builder)
-                 builder (assoc builder :labels (assoc (:labels builder) key label))
-                 builder (seal builder {:op :jump :target label})]
-             [(start builder label) {:op :literal :value nil}]))
-  :goto (let [builder (seal builder {:op :transfer :name (:name node) :site (:site expr)})
-              [builder next] (fresh-block builder)]
-          [(start builder next) {:op :literal :value nil}])
-  :do (let [[builder values] (lower-forms builder (:forms node))]
-        [builder (or (peek values) {:op :literal :value nil})])
-  :let (lower (lower-bindings builder (:bindings node)) (:body node)))
-
-(np/defpass lower
-  {:from [s/Ordered Expr] :to (t/HVec [l/Symbolic-Builder l/Symbolic-Atom]) :args [l/Symbolic-Builder] :body true}
-  [builder expr]
-  (lower-node builder expr (:node expr)))
-
-(np/defpass compile-graph
-  {:from [s/Ordered Expr] :to [l/Symbolic Program] :args [s/Bound-Supply] :body true}
-  [supply expr]
-  (let [[builder value] (lower {:supply supply :ops [] :next 1 :current 0 :blocks {} :labels {} :loops {}} expr)]
-    {:entry 0 :labels (:labels builder) :blocks (:blocks (seal builder {:op :return :value value}))}))
diff --git a/src/com/tailrecursion/blacktide/compiler/native.clj b/src/com/tailrecursion/blacktide/compiler/native.clj
deleted file mode 100644
index fdc6176..0000000
--- a/src/com/tailrecursion/blacktide/compiler/native.clj
+++ /dev/null
@@ -1,108 +0,0 @@
-(ns ^:typed.clojure com.tailrecursion.blacktide.compiler.native
-  "Analyze native free reads and recover their source types from machine storage.
-  Primitive source reads cannot observe the slot's box; reference reads retain
-  it. Internal bindings keep native inference."
-  (:require [clojure.set :as set]
-            [com.tailrecursion.nanopass :as np]
-            [com.tailrecursion.blacktide.compiler.source-languages :as s]
-            [com.tailrecursion.blacktide.compiler.languages :as l]
-            [com.tailrecursion.blacktide.compiler.collections :as collections]
-            [com.tailrecursion.blacktide.compiler.host-emit :as host]
-            [com.tailrecursion.blacktide.compiler.case-emit :as cases]
-            [com.tailrecursion.blacktide.compiler.jvm-languages :as j]
-            [typed.clojure :as-alias t]))
-
-(declare boxed-reads form)
-
-(np/defpass hinted-form {:from [l/Machine Code] :to [l/Machine Code] :args [j/JVM-Raw] :body true}
-  [hint code]
-  ;; Var return tags may be Class objects; source form tags cannot be.
-  (let [hint (if (class? hint) (symbol (.getName ^Class hint)) hint)]
-    (with-meta (if (symbol? code) code (apply list code))
-               (cond-> (meta code) hint (assoc :tag hint)))))
-
-(np/defpass reads-node {:from [s/Native Node] :to (t/Set t/Sym)
-                        :args [(t/Map t/Sym java.lang.Class)]}
-  [types node]
-  :literal #{} :global #{} :var #{}
-  :local (if (:external? (:local node)) #{} #{(:id (:local node))})
-  :host (into #{} (mapcat (partial boxed-reads types)) (:args node))
-  :invoke (into (boxed-reads types (:function node)) (mapcat (partial boxed-reads types)) (:args node))
-  :if (set/union (boxed-reads types (:test node)) (boxed-reads types (:then node)) (boxed-reads types (:else node)))
-  :case (into (boxed-reads types (:selector node)) (mapcat (partial boxed-reads types)) (:arms node))
-  :dispatch (boxed-reads types (:value node))
-  :throw (boxed-reads types (:value node))
-  :collection (into (if-let [metadata (:metadata node)] (boxed-reads types metadata) #{}) (mapcat (partial boxed-reads types)) (:items node))
-  :do (into #{} (mapcat (partial boxed-reads types)) (:forms node))
-  :recur (into #{} (mapcat (partial boxed-reads types)) (:args node))
-  :let (set/difference (into (boxed-reads types (:body node)) (mapcat #(boxed-reads types (:init %))) (:bindings node))
-                       (set (map #(get-in % [:local :id]) (:bindings node))))
-  :loop (set/difference (into (boxed-reads types (:body node)) (mapcat #(boxed-reads types (:init %))) (:bindings node))
-                        (set (map #(get-in % [:local :id]) (:bindings node)))))
-
-(np/defpass boxed-reads {:from [s/Native Expr] :to (t/Set t/Sym)
-                         :args [(t/Map t/Sym java.lang.Class)] :body true}
-  [types expr]
-  (let [node (:node expr) fact (:jvm expr)]
-    (if (and (= :local (:op node)) (:primitive? fact)
-             (= (:type fact) (get types (:id (:local node)))))
-      #{} (reads-node types node))))
-
-(np/defpass reads {:from [s/Native Expr] :to (t/Set t/Sym) :body true}
-  [expr] (boxed-reads {} expr))
-
-
-(np/defpass literal-values {:from [s/Native Exprs] :to (t/Option (t/Vec t/Any)) :body true}
-  [items]
-  (reduce (fn [^{::t/- (t/Option (t/Vec t/Any))} values ^{::t/- s/Native-Expr} expr]
-            (let [node (:node expr)]
-              (if (and values (= :literal (:op node))) (conj values (:value node)) (reduced nil)))) [] items))
-
-(np/defpass bindings-form
-  {:from [s/Native Bindings] :to (t/Vec t/Any) :args [(t/Set t/Sym)] :body true}
-  [bound bindings]
-  (vec (mapcat (fn [^{::t/- s/Native-Binding} binding]
-                 [(with-meta (:id (:local binding)) (meta (:name (:local binding))))
-                  (form bound (:init binding))]) bindings)))
-
-
-(np/defpass node-form
-  {:from [s/Native Node] :to [l/Machine Code] :args [(t/Set t/Sym) s/Native-Expr]}
-  [bound expr node]
-  :literal (list 'quote (:value node))
-  :global (host/typed-form (:jvm expr) (host/global-form (:name node)))
-  :var (list 'var (:name node))
-  :local (let [local (:local node) id (:id local)]
-           (if (contains? bound id)
-             (with-meta id (when-let [hint (:hint (:jvm expr))] {:tag hint}))
-             (host/typed-form (:jvm expr) (host/box-form id))))
-  :host (hinted-form (:hint (:jvm expr)) (host/host-form (mapv #(form bound %) (:args node)) (:operation node)))
-  :invoke (let [function (:function node) source (:form (:site function))
-                ;; Quote changes KeywordExpr into ConstantExpr, bypassing the
-                ;; source call site's IKeywordLookup cache. Preserve its syntax.
-                callee (if (and (keyword? source) (= :literal (:op (:node function))))
-                         source (form bound function))]
-            (hinted-form (:hint (:jvm expr)) (list* callee (mapv #(form bound %) (:args node)))))
-  :if (list 'if (form bound (:test node)) (form bound (:then node)) (form bound (:else node)))
-  :case (cases/arms-form (mapv #(form bound %) (:arms node)) (form bound (:selector node)))
-  :dispatch (cases/selector-form (form bound (:value node)) (:spec node))
-  :throw (list 'throw (form bound (:value node)))
-  :do (list* 'do (mapv #(form bound %) (:forms node)))
-  :recur (list* 'recur (mapv #(form bound %) (:args node)))
-  :let (let [bound (into bound (map #(:id (:local %))) (:bindings node))]
-         (list 'let* (bindings-form bound (:bindings node)) (form bound (:body node))))
-  :loop (let [bound (into bound (map #(:id (:local %))) (:bindings node))]
-          (list 'loop* (bindings-form bound (:bindings node)) (form bound (:body node))))
-  :collection
-  (let [items (:items node) metadata (:metadata node) values (literal-values items)
-        keys (when (= :map (:kind node)) (literal-values (vec (take-nth 2 items))))
-        unique? (boolean (and keys (= (count keys) (count (set keys)))))
-        _ (when (and (nil? metadata) keys (not unique?))
-            (throw (IllegalArgumentException. "Duplicate constant keys in map")))
-        value (if (and (nil? metadata) values)
-                (list 'quote (collections/constant values (:kind node)))
-                (collections/form (mapv #(form bound %) items) unique? (:kind node)))]
-    (if metadata (list '. value 'withMeta (form bound metadata)) value)))
-
-(np/defpass form {:from [s/Native Expr] :to [l/Machine Code] :args [(t/Set t/Sym)] :body true}
-  [bound expr] (node-form bound expr (:node expr)))
diff --git a/src/com/tailrecursion/blacktide/compiler/order.clj b/src/com/tailrecursion/blacktide/compiler/order.clj
deleted file mode 100644
index ea63a7e..0000000
--- a/src/com/tailrecursion/blacktide/compiler/order.clj
+++ /dev/null
@@ -1,116 +0,0 @@
-(ns ^:typed.clojure com.tailrecursion.blacktide.compiler.order
-  "Make operand evaluation explicit before CFG construction. Calls and collection
-  construction consume immutable saved locals; source scopes stay unchanged."
-  (:require [com.tailrecursion.nanopass :as np]
-            [com.tailrecursion.blacktide.compiler.source-languages :as l]
-            [com.tailrecursion.blacktide.compiler.source :as source]
-            [typed.clojure :as-alias t]))
-
-(declare normalize)
-
-(np/defpass save
-  {:from [l/Ordered Expr] :to (t/HVec [l/Bound-Supply l/Ordered-Binding l/Ordered-Local])
-   :args [l/Bound-Supply] :body true}
-  [supply expr]
-  (let [[supply id] (source/fresh "operand" supply)
-        local {:id id :name id :mutable? false :external? false :jvm (:jvm expr)}]
-    [supply {:local local :init expr} local]))
-
-(np/defpass operands
-  {:from [l/Prepared Exprs]
-   :to (t/HVec [l/Bound-Supply (t/Vec l/Ordered-Binding) (t/Vec l/Ordered-Local)])
-   :args [l/Bound-Supply] :body true}
-  [supply exprs]
-  (reduce (fn [^{::t/- (t/HVec [l/Bound-Supply (t/Vec l/Ordered-Binding) (t/Vec l/Ordered-Local)])} acc
-               ^{::t/- l/Prepared-Expr} expr]
-            (let [[supply bindings locals] acc
-                  [supply expr] (normalize supply expr)
-                  [supply binding local] (save supply expr)]
-              [supply (conj bindings binding) (conj locals local)])) [supply [] []] exprs))
-
-(np/defpass normalize-forms
-  {:from [l/Prepared Exprs] :to (t/HVec [l/Bound-Supply l/Ordered-Exprs])
-   :args [l/Bound-Supply] :body true}
-  [supply exprs]
-  (reduce (fn [^{::t/- (t/HVec [l/Bound-Supply l/Ordered-Exprs])} acc ^{::t/- l/Prepared-Expr} expr]
-            (let [[supply out] acc [supply expr] (normalize supply expr)] [supply (conj out expr)]))
-          [supply []] exprs))
-
-(np/defpass normalize-bindings
-  {:from [l/Prepared Bindings] :to (t/HVec [l/Bound-Supply l/Ordered-Bindings])
-   :args [l/Bound-Supply] :body true}
-  [supply bindings]
-  (reduce (fn [^{::t/- (t/HVec [l/Bound-Supply l/Ordered-Bindings])} acc ^{::t/- l/Prepared-Binding} binding]
-            (let [[supply out] acc [supply init] (normalize supply (:init binding))]
-              [supply (conj out (assoc binding :init init))])) [supply []] bindings))
-
-(np/defpass normalize-node
-  {:from [l/Prepared Node] :to (t/HVec [l/Bound-Supply l/Ordered-Expr])
-   :args [l/Bound-Supply l/Prepared-Expr]}
-  [supply expr node]
-  :literal [supply (assoc expr :node node)]
-  :local [supply (assoc expr :node node)]
-  :global [supply (assoc expr :node node)]
-  :var [supply (assoc expr :node node)]
-  :label [supply (assoc expr :node node)]
-  :goto [supply (assoc expr :node node)]
-  :native [supply (assoc expr :node node)]
-  :select (let [[supply selector] (normalize supply (:selector node))
-                [supply arms] (normalize-forms supply (:arms node))]
-            [supply (assoc expr :node {:op :select :selector selector :arms arms})])
-  :dispatch (let [[supply value] (normalize supply (:value node))
-                  [supply binding local] (save supply value)]
-              [supply (assoc expr :node {:op :let :bindings [binding]
-                                        :body (assoc expr :node {:op :dispatch :value local :spec (:spec node)})})])
-  :recur (let [[supply bindings args] (operands supply (:args node))]
-           [supply (assoc expr :node {:op :let :bindings bindings
-                                     :body (assoc expr :node {:op :recur :target (:target node) :args args})})])
-  :loop (let [[supply bindings] (normalize-bindings supply (:bindings node))
-              [supply body] (normalize supply (:body node))]
-          [supply (assoc expr :node {:op :loop :id (:id node) :bindings bindings :body body})])
-  :if (let [[supply test] (normalize supply (:test node))
-            [supply yes] (normalize supply (:then node))
-            [supply no] (normalize supply (:else node))]
-        [supply (assoc expr :node {:op :if :test test :then yes :else no})])
-  :throw (let [[supply value] (normalize supply (:value node))]
-           [supply (assoc expr :node {:op :throw :value value})])
-  :write (let [[supply value] (normalize supply (:value node))]
-           [supply (assoc expr :node {:op :write :target (:target node) :value value})])
-  :do (let [[supply forms] (normalize-forms supply (:forms node))]
-        [supply (assoc expr :node {:op :do :forms forms})])
-  :let (let [[supply bindings] (normalize-bindings supply (:bindings node))
-             [supply body] (normalize supply (:body node))]
-         [supply (assoc expr :node {:op :let :bindings bindings :body body})])
-  :convert
-  (let [[supply value] (normalize supply (:value node))
-        [supply binding local] (save supply value)]
-    [supply (assoc expr :node {:op :let :bindings [binding]
-                              :body (assoc expr :node {:op :convert :conversion (:conversion node) :value local})})])
-  :host-call
-  (let [[supply bindings args] (operands supply (:args node))]
-    [supply (assoc expr :node {:op :let :bindings bindings
-                              :body (assoc expr :node {:op :host-call :operation (:operation node) :args args})})])
-  :invoke
-  (let [[supply function] (normalize supply (:function node))
-        [supply binding local] (save supply function)
-        [supply bindings args] (operands supply (:args node))]
-    [supply (assoc expr :node {:op :let :bindings (into [binding] bindings)
-                              :body (assoc expr :node {:op :invoke :function local :args args})})])
-  :build
-  (let [[supply bindings items] (operands supply (:items node))
-        value (assoc expr :node {:op :build :kind (:kind node) :items items :unique? (:unique? node)})
-        [supply bindings value]
-        (if-let [metadata (:metadata node)]
-          (let [[supply binding local] (save supply value)
-                [supply metadata] (normalize supply metadata)
-                [supply meta-binding meta-local] (save supply metadata)]
-            [supply (into bindings [binding meta-binding])
-             (assoc expr :node {:op :metadata :value local :metadata meta-local})])
-          [supply bindings value])]
-    [supply (assoc expr :node {:op :let :bindings bindings :body value})]))
-
-(np/defpass normalize
-  {:from [l/Prepared Expr] :to (t/HVec [l/Bound-Supply l/Ordered-Expr])
-   :args [l/Bound-Supply] :body true}
-  [supply expr]
-  (normalize-node supply expr (:node expr)))
diff --git a/src/com/tailrecursion/blacktide/compiler/passes/assignment.clj b/src/com/tailrecursion/blacktide/compiler/passes/assignment.clj
new file mode 100644
index 0000000..118510f
--- /dev/null
+++ b/src/com/tailrecursion/blacktide/compiler/passes/assignment.clj
@@ -0,0 +1,123 @@
+(ns ^:typed.clojure com.tailrecursion.blacktide.compiler.passes.assignment
+  "Lower sequential batches to writes, then parallel batches to saved RHSs and
+  writes. Pending parallel writes do not execute if an RHS abandons evaluation."
+  (:require [com.tailrecursion.nanopass :as np]
+            [com.tailrecursion.blacktide.compiler.languages :as l]
+            [com.tailrecursion.blacktide.compiler.passes.source :as source]
+            [typed.clojure :as-alias t]))
+
+(np/defpasses
+  (sequential-pair
+    {:from [l/Classified Assignment] :to [l/Sequential Assignment]
+     :processors {Local clojure.core/identity Expr sequential}}
+    [pair])
+
+  (sequential-pairs
+    {:from [l/Classified Assignments] :to [l/Sequential Assignments] :processors {Assignment sequential-pair}}
+    [pairs])
+
+  (sequential-forms
+    {:from [l/Classified Exprs] :to [l/Sequential Exprs] :processors {Expr sequential}}
+    [forms])
+
+  (sequential-node
+    {:from [l/Classified Node] :to [l/Sequential Node]
+     :processors {Local clojure.core/identity Raw clojure.core/identity CollectionKind clojure.core/identity RecurTarget clojure.core/identity CaseSpec clojure.core/identity
+                  Expr sequential Exprs sequential-forms Binding sequential-binding Assignments sequential-pairs}}
+    [node]
+    :setq {:op :do :forms
+           (mapv (fn [pair]
+                   (let [value (sequential (:value pair))]
+                     (assoc value :node {:op :write :target (:target pair) :value value}))) (:pairs node))})
+
+  (sequential-binding
+    {:from [l/Classified Binding] :to [l/Sequential Binding]
+     :processors {Local clojure.core/identity Expr sequential}}
+    [binding])
+
+  (sequential
+    {:from [l/Classified Expr] :to [l/Sequential Expr] :body true}
+    [expr]
+    (assoc expr :node (sequential-node (:node expr)))))
+
+(np/defpasses
+  (parallel-forms
+    {:from [l/Sequential Exprs] :to (t/HVec [l/Bound-Supply l/Assigned-Exprs])
+     :args [l/Bound-Supply] :body true}
+    [supply forms]
+    (reduce (fn [^{::t/- (t/HVec [l/Bound-Supply l/Assigned-Exprs])} acc ^{::t/- l/Sequential-Expr} expr]
+              (let [[supply out] acc [supply expr] (parallel supply expr)] [supply (conj out expr)]))
+            [supply []] forms))
+
+  (parallel-bindings
+    {:from [l/Sequential Bindings] :to (t/HVec [l/Bound-Supply l/Assigned-Bindings])
+     :args [l/Bound-Supply] :body true}
+    [supply bindings]
+    (reduce (fn [^{::t/- (t/HVec [l/Bound-Supply l/Assigned-Bindings])} acc ^{::t/- l/Sequential-Binding} binding]
+              (let [[supply out] acc [supply init] (parallel supply (:init binding))]
+                [supply (conj out (assoc binding :init init))])) [supply []] bindings))
+
+  (save-parallel-values
+    {:from [l/Sequential Assignments]
+     :to (t/HVec [l/Bound-Supply l/Assigned-Bindings l/Assigned-Exprs]) :args [l/Bound-Supply] :body true}
+    [supply pairs]
+    (reduce (fn [^{::t/- (t/HVec [l/Bound-Supply l/Assigned-Bindings l/Assigned-Exprs])} acc
+                 ^{::t/- l/Sequential-Assignment} pair]
+              (let [[supply bindings writes] acc [supply value] (parallel supply (:value pair))
+                    [supply id] (source/fresh "parallel" supply)
+                    local {:id id :name id :mutable? false :external? false :jvm (:jvm value)}
+                    read (assoc value :node {:op :local :local local})
+                    write (assoc value :node {:op :write :target (:target pair) :value read})]
+                [supply (conj bindings {:local local :init value}) (conj writes write)])) [supply [] []] pairs))
+
+  (parallel-node
+    {:from [l/Sequential Node] :to (t/HVec [l/Bound-Supply l/Assigned-Node]) :args [l/Bound-Supply]}
+    [supply node]
+    :literal [supply node]
+    :local [supply node]
+    :global [supply node]
+    :var [supply node]
+    :label [supply node]
+    :goto [supply node]
+    :native [supply node]
+    :dispatch (let [[supply value] (parallel supply (:value node))]
+                [supply {:op :dispatch :value value :spec (:spec node)}])
+    :case (let [[supply selector] (parallel supply (:selector node))
+                [supply arms] (parallel-forms supply (:arms node))]
+            [supply {:op :case :selector selector :arms arms}])
+    :recur (let [[supply args] (parallel-forms supply (:args node))]
+             [supply {:op :recur :target (:target node) :args args}])
+    :loop (let [[supply bindings] (parallel-bindings supply (:bindings node))
+                [supply body] (parallel supply (:body node))]
+            [supply {:op :loop :id (:id node) :bindings bindings :body body}])
+    :if (let [[supply test] (parallel supply (:test node))
+              [supply yes] (parallel supply (:then node))
+              [supply no] (parallel supply (:else node))]
+          [supply {:op :if :test test :then yes :else no}])
+    :throw (let [[supply value] (parallel supply (:value node))] [supply {:op :throw :value value}])
+    :write (let [[supply value] (parallel supply (:value node))]
+             [supply {:op :write :target (:target node) :value value}])
+    :do (let [[supply forms] (parallel-forms supply (:forms node))] [supply {:op :do :forms forms}])
+    :host (let [[supply args] (parallel-forms supply (:args node))]
+            [supply {:op :host :operation (:operation node) :args args}])
+    :invoke (let [[supply function] (parallel supply (:function node))
+                  [supply args] (parallel-forms supply (:args node))]
+              [supply {:op :invoke :function function :args args}])
+    :collection (let [[supply items] (parallel-forms supply (:items node))
+                      [supply metadata] (if-let [metadata (:metadata node)] (parallel supply metadata) [supply nil])]
+                  [supply {:op :collection :kind (:kind node) :items items :metadata metadata}])
+    :let (let [[supply bindings] (parallel-bindings supply (:bindings node))
+               [supply body] (parallel supply (:body node))]
+           [supply {:op :let :bindings bindings :body body}])
+    :psetq (let [[supply bindings writes] (save-parallel-values supply (:pairs node))
+                 template (first writes)]
+             (if template
+               [supply {:op :let :bindings bindings
+                        :body (assoc template :node {:op :do :forms (conj writes (assoc template :node {:op :literal :value nil}))})}]
+               [supply {:op :literal :value nil}])))
+
+  (parallel
+    {:from [l/Sequential Expr] :to (t/HVec [l/Bound-Supply l/Assigned-Expr])
+     :args [l/Bound-Supply] :body true}
+    [supply expr]
+    (let [[supply node] (parallel-node supply (:node expr))] [supply (assoc expr :node node)])))
diff --git a/src/com/tailrecursion/blacktide/compiler/passes/boundaries.clj b/src/com/tailrecursion/blacktide/compiler/passes/boundaries.clj
new file mode 100644
index 0000000..ef7596d
--- /dev/null
+++ b/src/com/tailrecursion/blacktide/compiler/passes/boundaries.clj
@@ -0,0 +1,76 @@
+(ns ^:typed.clojure com.tailrecursion.blacktide.compiler.passes.boundaries
+  "Classify analyzed native bodies before expression lowering. Native subtrees
+  exclude DSL effects; lowered loops materialize their source representations."
+  (:require [com.tailrecursion.blacktide.compiler.passes.effects :refer [effects]]
+            [com.tailrecursion.nanopass :as np]
+            [com.tailrecursion.blacktide.compiler.languages :as l]
+            [com.tailrecursion.blacktide.compiler.passes.loop-facts :as loops]
+            [typed.clojure :as-alias t]))
+
+(np/defpasses
+  (native-node
+    {:from [l/Bound Node] :to [l/Native Node]
+     :processors {Expr native Exprs native-forms Binding native-binding}}
+    [node]
+    :literal node :local node :global node :var node
+    :case {:op :case :selector (native (:selector node))
+           :arms (native-forms (:arms node))}
+    :dispatch {:op :dispatch :value (native (:value node)) :spec (:spec node)}
+    :collection {:op :collection :kind (:kind node) :items (native-forms (:items node))
+                 :metadata (when-let [metadata (:metadata node)] (native metadata))}
+    :recur {:op :recur :target (:target node) :args (mapv native (:args node))}
+    :loop {:op :loop :id (:id node) :bindings (mapv native-binding (:bindings node))
+           :body (native (:body node))}
+    :setq (if (empty? (:pairs node)) {:op :literal :value nil} (throw (IllegalStateException. "Native local write")))
+    :psetq (if (empty? (:pairs node)) {:op :literal :value nil} (throw (IllegalStateException. "Native parallel write")))
+    :label (throw (IllegalStateException. "Native label"))
+    :goto (throw (IllegalStateException. "Native transfer")))
+
+  (native-binding {:from [l/Bound Binding] :to [l/Native Binding] :body true}
+    [binding] (assoc binding :init (native (:init binding))))
+
+  (native-forms {:from [l/Bound Exprs] :to [l/Native Exprs] :processors {Expr native}}
+    [forms])
+
+  (native {:from [l/Bound Expr] :to [l/Native Expr] :body true}
+    [expr]
+    (assoc expr :node (native-node (:node expr)))))
+
+(np/defpasses
+  (classify-node
+    {:from [l/Bound Node] :to [l/Classified Node]
+     :processors {Expr classify Exprs classify-forms Binding classify-binding Assignments classify-pairs
+                  Local clojure.core/identity RecurTarget clojure.core/identity
+                  Raw clojure.core/identity CollectionKind clojure.core/identity CaseSpec clojure.core/identity}}
+    [node]
+    :loop {:op :loop :id (:id node) :bindings (mapv classify-binding (:bindings node)) :body (classify (:body node))})
+
+  (classify-pair {:from [l/Bound Assignment] :to [l/Classified Assignment]
+                            :processors {Expr classify Local clojure.core/identity}}
+    [pair])
+
+  (classify-pairs {:from [l/Bound Assignments] :to [l/Classified Assignments] :processors {Assignment classify-pair}}
+    [pairs])
+
+  (classify-binding {:from [l/Bound Binding] :to [l/Classified Binding]
+                               :processors {Expr classify Local clojure.core/identity}}
+    [binding])
+
+  (classify-forms {:from [l/Bound Exprs] :to [l/Classified Exprs] :processors {Expr classify}}
+    [forms])
+
+  (classify {:from [l/Bound Expr] :to [l/Classified Expr] :body true}
+    [expr]
+    (let [node (:node expr)]
+      (cond
+        ;; KeywordInvokeExpr snapshots its cache before evaluating the target.
+        ;; Retain the whole call when the target needs no DSL control or writes;
+        ;; saving the target first would change reentrant lookup behavior.
+        (and (= :invoke (:op node)) (= 1 (count (:args node)))
+             (= :literal (:op (:node (:function node))))
+             (keyword? (:form (:site (:function node)))) (not (effects expr)))
+        (assoc expr :node {:op :native :value (native expr)})
+        (and (= :loop (:op node)) (not (effects expr)))
+        (assoc expr :node {:op :native :value (native expr)})
+        (= :loop (:op node)) (let [expr (loops/lower expr)] (assoc expr :node (classify-node (:node expr))))
+        :else (assoc expr :node (classify-node node))))))
diff --git a/src/com/tailrecursion/blacktide/compiler/box_origins.clj b/src/com/tailrecursion/blacktide/compiler/passes/box_origins.clj
similarity index 86%
rename from src/com/tailrecursion/blacktide/compiler/box_origins.clj
rename to src/com/tailrecursion/blacktide/compiler/passes/box_origins.clj
index 3c78396..3d8cbbf 100644
--- a/src/com/tailrecursion/blacktide/compiler/box_origins.clj
+++ b/src/com/tailrecursion/blacktide/compiler/passes/box_origins.clj
@@ -1,12 +1,11 @@
-(ns ^:typed.clojure com.tailrecursion.blacktide.compiler.box-origins
+(ns ^:typed.clojure com.tailrecursion.blacktide.compiler.passes.box-origins
   "Find values that can be reboxed at return, following copies. Primitive
   computations create boxes; compiled small Long literals use the guaranteed
   cache. Storage analysis must still exclude all other identity observations."
   (:require [clojure.set :as set]
             [com.tailrecursion.nanopass :as np]
             [com.tailrecursion.blacktide.compiler.languages :as l]
-            [com.tailrecursion.blacktide.compiler.analysis-languages :as a]
-            [com.tailrecursion.blacktide.compiler.value-facts :as facts]
+            [com.tailrecursion.blacktide.compiler.passes.value-facts :as facts]
             [typed.clojure :as-alias t]))
 
 (np/defpass copy-source {:from [l/Machine Rhs] :to (t/Option t/Sym) :body true}
@@ -22,7 +21,7 @@
     (:host :convert) (class? (facts/rhs-type {} rhs))
     false))
 
-(np/defpass reboxable-values {:from [a/Analysis Program] :to (t/Set t/Sym) :body true}
+(np/defpass reboxable-values {:from [l/Analysis Program] :to (t/Set t/Sym) :body true}
   [analysis]
   (let [definitions (facts/definitions (:reachable analysis) (:machine analysis))
         initial (reduce-kv
diff --git a/src/com/tailrecursion/blacktide/compiler/case_emit.clj b/src/com/tailrecursion/blacktide/compiler/passes/case_emit.clj
similarity index 85%
rename from src/com/tailrecursion/blacktide/compiler/case_emit.clj
rename to src/com/tailrecursion/blacktide/compiler/passes/case_emit.clj
index bf670f6..449cfbc 100644
--- a/src/com/tailrecursion/blacktide/compiler/case_emit.clj
+++ b/src/com/tailrecursion/blacktide/compiler/passes/case_emit.clj
@@ -1,13 +1,12 @@
-(ns ^:typed.clojure com.tailrecursion.blacktide.compiler.case-emit
+(ns ^:typed.clojure com.tailrecursion.blacktide.compiler.passes.case-emit
   "Emit native case* dispatch from constant table data. Result arms are separate
   from constant matching; selection never substitutes ordinary equality for case."
   (:require [com.tailrecursion.nanopass :as np]
-            [com.tailrecursion.blacktide.compiler.source-languages :as s]
             [com.tailrecursion.blacktide.compiler.languages :as l]
             [typed.clojure :as-alias t]))
 
 (np/defpass selector-form
-  {:from [s/Bound CaseSpec] :to [l/Machine Code] :args [l/Machine-Code] :body true}
+  {:from [l/Bound CaseSpec] :to [l/Machine Code] :args [l/Machine-Code] :body true}
   [value spec]
   (let [local (gensym "dispatch__")]
     (list 'let* [local value]
diff --git a/src/com/tailrecursion/blacktide/compiler/passes/cases.clj b/src/com/tailrecursion/blacktide/compiler/passes/cases.clj
new file mode 100644
index 0000000..2718523
--- /dev/null
+++ b/src/com/tailrecursion/blacktide/compiler/passes/cases.clj
@@ -0,0 +1,25 @@
+(ns ^:typed.clojure com.tailrecursion.blacktide.compiler.passes.cases
+  "Separate source case from ordinal arm selection after native constant dispatch.
+  Each source arm occurs once, including grouped and hash-collision alternatives."
+  (:require [com.tailrecursion.nanopass :as np]
+            [com.tailrecursion.blacktide.compiler.languages :as l]))
+
+(np/defpasses
+  (normalize-node
+    {:from [l/Assigned Node] :to [l/Cases Node]
+     :processors {Expr normalize Exprs normalize-forms Binding normalize-binding
+                  Raw clojure.core/identity Local clojure.core/identity CollectionKind clojure.core/identity
+                  RecurTarget clojure.core/identity CaseSpec clojure.core/identity}}
+    [node]
+    :case
+    {:op :select :selector (normalize (:selector node)) :arms (normalize-forms (:arms node))})
+
+  (normalize-binding {:from [l/Assigned Binding] :to [l/Cases Binding]
+                                :processors {Expr normalize Local clojure.core/identity}}
+    [binding])
+
+  (normalize-forms {:from [l/Assigned Exprs] :to [l/Cases Exprs] :processors {Expr normalize}}
+    [forms])
+
+  (normalize {:from [l/Assigned Expr] :to [l/Cases Expr] :body true}
+    [expr] (assoc expr :node (normalize-node (:node expr)))))
diff --git a/src/com/tailrecursion/blacktide/compiler/passes/collections.clj b/src/com/tailrecursion/blacktide/compiler/passes/collections.clj
new file mode 100644
index 0000000..04fbc12
--- /dev/null
+++ b/src/com/tailrecursion/blacktide/compiler/passes/collections.clj
@@ -0,0 +1,69 @@
+(ns ^:typed.clojure com.tailrecursion.blacktide.compiler.passes.collections
+  "Preserve native constant collections and construction checks. Metadata keeps
+  construction dynamic and is evaluated only after that construction succeeds."
+  (:require [com.tailrecursion.nanopass :as np]
+            [com.tailrecursion.blacktide.compiler.languages :as l]
+            [com.tailrecursion.blacktide.compiler.passes.host-emit :as host]
+            [typed.clojure :as-alias t]))
+
+(np/defpass constant
+  {:from [l/Collections CollectionKind] :to t/Any :args [(t/Vec t/Any)] :body true}
+  [values kind]
+  (case kind
+    :vector (vec values)
+    :map (reduce (fn [^{::t/- (t/Map t/Any t/Any)} out ^{::t/- (t/Seq t/Any)} pair]
+                   (assoc out (first pair) (second pair))) {} (partition 2 values))
+    :set (set values)))
+
+(np/defpass form
+  {:from [l/Collections CollectionKind] :to [l/Machine Code]
+   :args [(t/Vec l/Machine-Code) t/Bool] :body true}
+  [items unique? kind]
+  (if (= :vector kind)
+    (if (<= (count items) 6)
+      (list* 'clojure.lang.Tuple/create items)
+      ;; An Object expression prevents constant folding without unboxing the
+      ;; first literal, which would lose its original numeric width or identity.
+      (list 'do (assoc items 0 (host/box-form (nth items 0)))))
+    (list (if (= :set kind) 'clojure.lang.RT/set
+              (if unique? 'clojure.lang.RT/mapUniqueKeys 'clojure.lang.RT/map))
+          (list 'clojure.lang.RT/object_array items))))
+
+(np/defpass literal-values
+  {:from [l/Collections Exprs] :to (t/Option (t/Vec t/Any)) :body true}
+  [items]
+  (reduce (fn [^{::t/- (t/Option (t/Vec t/Any))} values ^{::t/- l/Collections-Expr} expr]
+            (let [node (:node expr)]
+              (if (and values (= :literal (:op node))) (conj values (:value node)) (reduced nil))))
+          [] items))
+
+(np/defpasses
+  (normalize-forms
+    {:from [l/Cases Exprs] :to [l/Collections Exprs] :processors {Expr normalize}}
+    [forms])
+
+  (normalize-node
+    {:from [l/Cases Node] :to [l/Collections Node]
+     :processors {Expr normalize Exprs normalize-forms Binding normalize-binding Raw clojure.core/identity
+                  Local clojure.core/identity RecurTarget clojure.core/identity CaseSpec clojure.core/identity}}
+    [node]
+    :collection
+    (let [items (mapv normalize (:items node)) metadata (some-> (:metadata node) normalize)
+          values (literal-values items)
+          keys (when (= :map (:kind node)) (literal-values (vec (take-nth 2 items))))
+          unique? (boolean (and keys (= (count keys) (count (set keys)))))]
+      (when (and (nil? metadata) keys (not unique?))
+        (throw (IllegalArgumentException. "Duplicate constant keys in map")))
+      (if (and (nil? metadata) values)
+        {:op :literal :value (constant values (:kind node))}
+        {:op :build :kind (:kind node) :items items :metadata metadata :unique? unique?})))
+
+  (normalize-binding
+    {:from [l/Cases Binding] :to [l/Collections Binding]
+     :processors {Expr normalize Local clojure.core/identity}}
+    [binding])
+
+  (normalize
+    {:from [l/Cases Expr] :to [l/Collections Expr] :body true}
+    [expr]
+    (assoc expr :node (normalize-node (:node expr)))))
diff --git a/src/com/tailrecursion/blacktide/compiler/compact.clj b/src/com/tailrecursion/blacktide/compiler/passes/compact.clj
similarity index 75%
rename from src/com/tailrecursion/blacktide/compiler/compact.clj
rename to src/com/tailrecursion/blacktide/compiler/passes/compact.clj
index ea027c1..372317c 100644
--- a/src/com/tailrecursion/blacktide/compiler/compact.clj
+++ b/src/com/tailrecursion/blacktide/compiler/passes/compact.clj
@@ -1,16 +1,15 @@
-(ns ^:typed.clojure com.tailrecursion.blacktide.compiler.compact
+(ns ^:typed.clojure com.tailrecursion.blacktide.compiler.passes.compact
   "Discard unused storage without discarding evaluation. Substitute compatible
   copies and single-use, total primitive computations only before their inputs
   are rebound. Native bodies remain opaque, including their free local reads."
   (:require [clojure.set :as set]
             [com.tailrecursion.nanopass :as np]
-            [com.tailrecursion.blacktide.compiler.structured-languages :as s]
-            [com.tailrecursion.blacktide.compiler.compact-languages :as c]
-            [com.tailrecursion.blacktide.compiler.native :as native]
+            [com.tailrecursion.blacktide.compiler.languages :as l]
+            [com.tailrecursion.blacktide.compiler.passes.native :as native]
             [com.tailrecursion.blacktide.compiler.jvm :as jvm]
             [typed.clojure :as-alias t]))
 
-(np/defpass introduce {:from [s/Stored Expr] :to [c/Compact Expr]}
+(np/defpass introduce {:from [l/Stored Expr] :to [l/Compact Expr]}
   [node]
   :bind (assoc node :body (introduce (:body node)))
   :join (assoc node :value (introduce (:value node)) :body (introduce (:body node)))
@@ -21,38 +20,38 @@
   :recur {:op :recur :args (mapv (fn [id] {:op :local :id id}) (:slots node))}
   :return node :throw node)
 
-(declare rhs-reads)
-(np/defpass atom-reads {:from [c/Compact Atom] :to (t/Vec t/Sym)}
-  [node]
-  :local [(:id node)] :literal [] :inline (rhs-reads (:value node)))
-
-(np/defpass rhs-reads {:from [c/Compact Rhs] :to (t/Vec t/Sym)}
-  [node]
-  :value (atom-reads (:value node))
-  :global [] :external [] :var []
-  :native (vec (native/reads (:value node)))
-  :dispatch (atom-reads (:value (:value node)))
-  :convert (atom-reads (:value (:value node)))
-  :construct (into [] (mapcat atom-reads) (:items node))
-  :metadata (into (atom-reads (:value node)) (atom-reads (:metadata node)))
-  :host (into [] (mapcat #(atom-reads (:value %))) (:args node))
-  :invoke (into (atom-reads (:function node)) (mapcat atom-reads) (:args node)))
-
-(np/defpass reads {:from [c/Compact Expr] :to (t/Vec t/Sym)}
+(np/defpasses
+  (atom-reads {:from [l/Compact Atom] :to (t/Vec t/Sym)}
+    [node]
+    :local [(:id node)] :literal [] :inline (rhs-reads (:value node)))
+
+  (rhs-reads {:from [l/Compact Rhs] :to (t/Vec t/Sym)}
+    [node]
+    :value (atom-reads (:value node))
+    :global [] :external [] :var []
+    :native (vec (native/reads (:value node)))
+    :dispatch (atom-reads (:value (:value node)))
+    :convert (atom-reads (:value (:value node)))
+    :construct (into [] (mapcat atom-reads) (:items node))
+    :metadata (into (atom-reads (:value node)) (atom-reads (:metadata node)))
+    :host (into [] (mapcat #(atom-reads (:value %))) (:args node))
+    :invoke (into (atom-reads (:function node)) (mapcat atom-reads) (:args node))))
+
+(np/defpass reads {:from [l/Compact Expr] :to (t/Vec t/Sym)}
   [node]
   :bind (into (rhs-reads (:value (:binding node))) (filterv #(not= % (:target (:binding node))) (reads (:body node))))
   :effect (into (rhs-reads (:value (:binding node))) (reads (:body node)))
   :join (into (reads (:value node)) (filterv #(not= % (:target node)) (reads (:body node))))
   :branch (into (into (atom-reads (:test node)) (reads (:yes node))) (reads (:no node)))
   :switch (into (atom-reads (:test node)) (mapcat reads) (:arms node))
-  :loop (reduce (fn [^{::t/- (t/Vec t/Sym)} refs ^{::t/- c/Compact-Binding} binding]
+  :loop (reduce (fn [^{::t/- (t/Vec t/Sym)} refs ^{::t/- l/Compact-Binding} binding]
                   (into (atom-reads (:value binding)) (filterv #(not= % (:target binding)) refs)))
                 (reads (:body node)) (rseq (:bindings node)))
   :recur (into [] (mapcat atom-reads) (:args node))
   :return (atom-reads (:value node)) :throw (atom-reads (:value node)))
 
 (np/defpass stable?
-  {:from [c/Compact Expr] :to t/Bool :args [t/Sym (t/Set t/Sym)]}
+  {:from [l/Compact Expr] :to t/Bool :args [t/Sym (t/Set t/Sym)]}
   [id dependencies node]
   :bind (if (contains? dependencies (:target (:binding node)))
           (not (some #{id} (reads (:body node)))) (stable? id dependencies (:body node)))
@@ -68,64 +67,22 @@
              (stable? id dependencies (:body node)))
   :recur true :return true :throw true)
 
-(declare substitute-rhs)
-(np/defpass substitute-atom
-  {:from [c/Compact Atom] :to [c/Compact Atom] :args [t/Sym c/Compact-Atom]
-   :processors {Rhs substitute-rhs}}
-  [from replacement node]
-  :local (if (= from (:id node)) replacement node))
-
-(np/defpass substitute-operand
-  {:from [c/Compact Operand] :to [c/Compact Operand] :args [t/Sym c/Compact-Atom]
-   :processors {Atom substitute-atom}} [from replacement node])
-
-(np/defpass substitute-rhs
-  {:from [c/Compact Rhs] :to [c/Compact Rhs] :args [t/Sym c/Compact-Atom]
-   :processors {Atom substitute-atom Operand substitute-operand}} [from replacement node])
-
-(np/defpass substitute-operation
-  {:from [c/Compact Operation] :to [c/Compact Operation] :args [t/Sym c/Compact-Atom]
-   :processors {Rhs substitute-rhs}} [from replacement node])
-
-(np/defpass substitute-binding
-  {:from [c/Compact Binding] :to [c/Compact Binding] :args [t/Sym c/Compact-Atom]
-   :processors {Atom substitute-atom}} [from replacement node])
-
-(np/defpass substitute
-  {:from [c/Compact Expr] :to [c/Compact Expr] :args [t/Sym c/Compact-Atom]
-   :processors {Atom substitute-atom Rhs substitute-rhs Operation substitute-operation Binding substitute-binding}}
-  [from replacement node]
-  :bind (assoc node :binding (substitute-operation from replacement (:binding node))
-                   :body (if (= from (:target (:binding node))) (:body node)
-                           (substitute from replacement (:body node))))
-  :join (assoc node :value (substitute from replacement (:value node))
-                   :body (if (= from (:target node)) (:body node)
-                           (substitute from replacement (:body node))))
-  ;; Loop initializers see earlier bindings, but the body sees all loop binders.
-  :loop ((fn ^{::t/- [t/Int (t/Vec c/Compact-Binding) t/Bool :-> c/Compact-Expr]} bindings [index pairs shadowed?]
-           (if (< index (count (:bindings node)))
-             (let [binding (nth (:bindings node) index)]
-               (recur (inc index) (conj pairs (if shadowed? binding (substitute-binding from replacement binding)))
-                      (or shadowed? (= from (:target binding)))))
-             (assoc node :bindings pairs :body (if shadowed? (:body node)
-                                                (substitute from replacement (:body node)))))) 0 [] false))
-
 (np/defpass atom-type
-  {:from [c/Compact Atom] :to (t/Option java.lang.Class) :args [c/Compact-Types]}
+  {:from [l/Compact Atom] :to (t/Option java.lang.Class) :args [l/Compact-Types]}
   [types node]
   :local (get types (:id node))
   :literal (cond (instance? Long (:value node)) Long/TYPE (instance? Double (:value node)) Double/TYPE :else nil)
   :inline (:type node))
 
 (np/defpass movable?
-  {:from [c/Compact Rhs] :to t/Bool :args [c/Compact-Types] :body true}
+  {:from [l/Compact Rhs] :to t/Bool :args [l/Compact-Types] :body true}
   [types rhs]
   (and (= :host (:op rhs))
        (let [operation (:operation rhs)]
          (and (= :static (:op operation)) (= clojure.lang.Numbers (:class operation))
               (contains? '#{lt lte gt gte equiv unchecked_add unchecked_minus unchecked_multiply
                             unchecked_inc unchecked_dec unchecked_negate} (:member operation))
-              (every? (fn [^{::t/- c/Compact-Operand} operand]
+              (every? (fn [^{::t/- l/Compact-Operand} operand]
                         (let [type (atom-type types (:value operand))]
                           (and (contains? #{Long/TYPE Double/TYPE} type) (= type (:type (:jvm operand))))))
                       (:args rhs))
@@ -133,7 +90,7 @@
                 (and (class? type) (.isPrimitive ^Class type) (not= Void/TYPE type)))))))
 
 (np/defpass replacement
-  {:from [c/Compact Operation] :to (t/Option c/Compact-Atom) :args [c/Compact-Types t/Int] :body true}
+  {:from [l/Compact Operation] :to (t/Option l/Compact-Atom) :args [l/Compact-Types t/Int] :body true}
   [types uses binding]
   (let [rhs (:value binding) ^Class type (get types (:target binding))]
     (case (:op rhs)
@@ -160,14 +117,56 @@
                 (when (and type (= uses 1) (movable? types rhs)) {:op :inline :value rhs :type type})))
       nil)))
 
-(np/defpass discardable? {:from [c/Compact Rhs] :to t/Bool :body true}
+(np/defpasses
+  (substitute-atom
+    {:from [l/Compact Atom] :to [l/Compact Atom] :args [t/Sym l/Compact-Atom]
+     :processors {Rhs substitute-rhs}}
+    [from replacement node]
+    :local (if (= from (:id node)) replacement node))
+
+  (substitute-operand
+    {:from [l/Compact Operand] :to [l/Compact Operand] :args [t/Sym l/Compact-Atom]
+     :processors {Atom substitute-atom}} [from replacement node])
+
+  (substitute-rhs
+    {:from [l/Compact Rhs] :to [l/Compact Rhs] :args [t/Sym l/Compact-Atom]
+     :processors {Atom substitute-atom Operand substitute-operand}} [from replacement node]))
+
+(np/defpass substitute-operation
+  {:from [l/Compact Operation] :to [l/Compact Operation] :args [t/Sym l/Compact-Atom]
+   :processors {Rhs substitute-rhs}} [from replacement node])
+
+(np/defpass substitute-binding
+  {:from [l/Compact Binding] :to [l/Compact Binding] :args [t/Sym l/Compact-Atom]
+   :processors {Atom substitute-atom}} [from replacement node])
+
+(np/defpass substitute
+  {:from [l/Compact Expr] :to [l/Compact Expr] :args [t/Sym l/Compact-Atom]
+   :processors {Atom substitute-atom Rhs substitute-rhs Operation substitute-operation Binding substitute-binding}}
+  [from replacement node]
+  :bind (assoc node :binding (substitute-operation from replacement (:binding node))
+                   :body (if (= from (:target (:binding node))) (:body node)
+                           (substitute from replacement (:body node))))
+  :join (assoc node :value (substitute from replacement (:value node))
+                   :body (if (= from (:target node)) (:body node)
+                           (substitute from replacement (:body node))))
+  ;; Loop initializers see earlier bindings, but the body sees all loop binders.
+  :loop ((fn ^{::t/- [t/Int (t/Vec l/Compact-Binding) t/Bool :-> l/Compact-Expr]} bindings [index pairs shadowed?]
+           (if (< index (count (:bindings node)))
+             (let [binding (nth (:bindings node) index)]
+               (recur (inc index) (conj pairs (if shadowed? binding (substitute-binding from replacement binding)))
+                      (or shadowed? (= from (:target binding)))))
+             (assoc node :bindings pairs :body (if shadowed? (:body node)
+                                                (substitute from replacement (:body node)))))) 0 [] false))
+
+(np/defpass discardable? {:from [l/Compact Rhs] :to t/Bool :body true}
   [rhs]
   ;; Keep native validation, including constants that may fail AOT embedding.
   (and (= :value (:op rhs))
        (let [value (:value rhs)]
          (or (= :local (:op value)) (and (= :literal (:op value)) (nil? (:value value)))))))
 
-(np/defpass simplify {:from [c/Compact Expr] :to [c/Compact Expr] :args [c/Compact-Types]}
+(np/defpass simplify {:from [l/Compact Expr] :to [l/Compact Expr] :args [l/Compact-Types]}
   [types node]
   :bind (let [binding (:binding node) id (:target binding) body (simplify types (:body node))
               uses (count (filter #{id} (reads body)))
@@ -192,6 +191,6 @@
   :loop (assoc node :body (simplify types (:body node)))
   :recur node :return node :throw node)
 
-(np/defpass program {:from [s/Stored Program] :to [c/Compact Program] :body true}
+(np/defpass program {:from [l/Stored Program] :to [l/Compact Program] :body true}
   [program]
   {:types (:types program) :body (simplify (:types program) (introduce (:body program)))})
diff --git a/src/com/tailrecursion/blacktide/compiler/passes/effects.clj b/src/com/tailrecursion/blacktide/compiler/passes/effects.clj
new file mode 100644
index 0000000..9c6e9ea
--- /dev/null
+++ b/src/com/tailrecursion/blacktide/compiler/passes/effects.clj
@@ -0,0 +1,27 @@
+(ns ^:typed.clojure com.tailrecursion.blacktide.compiler.passes.effects
+  "Identify local writes and transfers that prevent native subtree emission."
+  (:require [com.tailrecursion.nanopass :as np]
+            [com.tailrecursion.blacktide.compiler.languages :as l]
+            [typed.clojure :as-alias t]))
+
+(np/defpasses
+  (effect-node {:from [l/Bound Node] :to t/Bool}
+    [node]
+    :literal false :local false :global false :var false
+    :setq (boolean (seq (:pairs node)))
+    :psetq (boolean (seq (:pairs node)))
+    :label true :goto true
+    :if (or (effects (:test node)) (effects (:then node)) (effects (:else node)))
+    :case (or (effects (:selector node)) (boolean (some effects (:arms node))))
+    :dispatch (effects (:value node))
+    :throw (effects (:value node))
+    :host (boolean (some effects (:args node)))
+    :invoke (or (effects (:function node)) (boolean (some effects (:args node))))
+    :collection (or (boolean (some effects (:items node))) (boolean (some-> (:metadata node) effects)))
+    :do (boolean (some effects (:forms node)))
+    :let (or (boolean (some #(effects (:init %)) (:bindings node))) (effects (:body node)))
+    :loop (or (boolean (some #(effects (:init %)) (:bindings node))) (effects (:body node)))
+    :recur (boolean (some effects (:args node))))
+
+  (effects {:from [l/Bound Expr] :to t/Bool :body true}
+    [expr] (effect-node (:node expr))))
diff --git a/src/com/tailrecursion/blacktide/compiler/emit.clj b/src/com/tailrecursion/blacktide/compiler/passes/emit.clj
similarity index 90%
rename from src/com/tailrecursion/blacktide/compiler/emit.clj
rename to src/com/tailrecursion/blacktide/compiler/passes/emit.clj
index 7492023..138f12b 100644
--- a/src/com/tailrecursion/blacktide/compiler/emit.clj
+++ b/src/com/tailrecursion/blacktide/compiler/passes/emit.clj
@@ -1,13 +1,13 @@
-(ns ^:typed.clojure com.tailrecursion.blacktide.compiler.emit
+(ns ^:typed.clojure com.tailrecursion.blacktide.compiler.passes.emit
   "Emit allocated machines as ordinary Clojure forms. Source effects occur in
   sequential lets; edges carry live values through boxed loop bindings."
   (:require [com.tailrecursion.nanopass :as np]
             [com.tailrecursion.blacktide.compiler.languages :as l]
-            [com.tailrecursion.blacktide.compiler.native :as native]
-            [com.tailrecursion.blacktide.compiler.collections :as collections]
-            [com.tailrecursion.blacktide.compiler.case-emit :as cases]
+            [com.tailrecursion.blacktide.compiler.passes.native :as native]
+            [com.tailrecursion.blacktide.compiler.passes.collections :as collections]
+            [com.tailrecursion.blacktide.compiler.passes.case-emit :as cases]
             [typed.clojure :as-alias t]
-            [com.tailrecursion.blacktide.compiler.host-emit :as host]))
+            [com.tailrecursion.blacktide.compiler.passes.host-emit :as host]))
 
 (np/defpass atom-form {:from [l/Machine Atom] :to [l/Machine Code]}
   [node]
diff --git a/src/com/tailrecursion/blacktide/compiler/feasible.clj b/src/com/tailrecursion/blacktide/compiler/passes/feasible.clj
similarity index 92%
rename from src/com/tailrecursion/blacktide/compiler/feasible.clj
rename to src/com/tailrecursion/blacktide/compiler/passes/feasible.clj
index b2a138d..c5e0999 100644
--- a/src/com/tailrecursion/blacktide/compiler/feasible.clj
+++ b/src/com/tailrecursion/blacktide/compiler/passes/feasible.clj
@@ -1,4 +1,4 @@
-(ns ^:typed.clojure com.tailrecursion.blacktide.compiler.feasible
+(ns ^:typed.clojure com.tailrecursion.blacktide.compiler.passes.feasible
   "Fold literal truth tests after source validation. Keep every block so dead
   source still receives JVM validation."
   (:require [com.tailrecursion.nanopass :as np]
diff --git a/src/com/tailrecursion/blacktide/compiler/graph.clj b/src/com/tailrecursion/blacktide/compiler/passes/graph.clj
similarity index 97%
rename from src/com/tailrecursion/blacktide/compiler/graph.clj
rename to src/com/tailrecursion/blacktide/compiler/passes/graph.clj
index ace2765..ba56c9b 100644
--- a/src/com/tailrecursion/blacktide/compiler/graph.clj
+++ b/src/com/tailrecursion/blacktide/compiler/passes/graph.clj
@@ -1,10 +1,10 @@
-(ns ^:typed.clojure com.tailrecursion.blacktide.compiler.graph
+(ns ^:typed.clojure com.tailrecursion.blacktide.compiler.passes.graph
   "Check initialization on reachable paths; retain all blocks for native Clojure
   validation. Liveness carries values through edges without evaluating dead code."
   (:require [clojure.set :as set]
             [com.tailrecursion.nanopass :as np]
             [com.tailrecursion.blacktide.compiler.languages :as l]
-            [com.tailrecursion.blacktide.compiler.native :as native]
+            [com.tailrecursion.blacktide.compiler.passes.native :as native]
             [typed.clojure :as-alias t]))
 
 (np/defpass atom-reads {:from [l/CFG Atom] :to (t/Set t/Sym)}
diff --git a/src/com/tailrecursion/blacktide/compiler/host_emit.clj b/src/com/tailrecursion/blacktide/compiler/passes/host_emit.clj
similarity index 93%
rename from src/com/tailrecursion/blacktide/compiler/host_emit.clj
rename to src/com/tailrecursion/blacktide/compiler/passes/host_emit.clj
index ae9cd66..178c380 100644
--- a/src/com/tailrecursion/blacktide/compiler/host_emit.clj
+++ b/src/com/tailrecursion/blacktide/compiler/passes/host_emit.clj
@@ -1,8 +1,7 @@
-(ns ^:typed.clojure com.tailrecursion.blacktide.compiler.host-emit
+(ns ^:typed.clojure com.tailrecursion.blacktide.compiler.passes.host-emit
   "Emit host descriptors with source-typed saved operands. Known nil is retained
   explicitly; Object prevents boxed storage from inventing a more specific type."
   (:require [com.tailrecursion.nanopass :as np]
-            [com.tailrecursion.blacktide.compiler.jvm-languages :as j]
             [com.tailrecursion.blacktide.compiler.languages :as l]
             [com.tailrecursion.blacktide.runtime]
             [typed.clojure :as-alias t]))
@@ -21,7 +20,7 @@
   (list (with-meta 'clojure.lang.RT/box {:param-tags ['java.lang.Object]}) form))
 
 (np/defpass typed-form
-  {:from [l/Machine Code] :to [l/Machine Code] :args [j/JVM-Fact] :body true}
+  {:from [l/Machine Code] :to [l/Machine Code] :args [l/JVM-Fact] :body true}
   [fact form]
   (let [^Class type (:type fact)
         object-form (with-meta (if (symbol? form) form (apply list form)) {:tag 'java.lang.Object})]
@@ -33,7 +32,7 @@
                        {:tag (if (class? type) (symbol (.getName type)) 'java.lang.Object)}))))
 
 (np/defpass conversion-form
-  {:from [l/Machine Code] :to [l/Machine Code] :args [j/JVM-Conversion j/JVM-Fact] :body true}
+  {:from [l/Machine Code] :to [l/Machine Code] :args [l/JVM-Conversion l/JVM-Fact] :body true}
   [conversion fact form]
   (let [^Class type (:type conversion)]
     (cond
@@ -50,7 +49,7 @@
 
 
 (np/defpass host-form
-  {:from [j/JVM Operation] :to [l/Machine Code] :args [(t/Vec l/Machine-Code)]}
+  {:from [l/JVM Operation] :to [l/Machine Code] :args [(t/Vec l/Machine-Code)]}
   [args operation]
   :keyword-site (list 'com.tailrecursion.blacktide.runtime/keyword-site (:key operation))
   :protocol
diff --git a/src/com/tailrecursion/blacktide/compiler/invariants.clj b/src/com/tailrecursion/blacktide/compiler/passes/invariants.clj
similarity index 83%
rename from src/com/tailrecursion/blacktide/compiler/invariants.clj
rename to src/com/tailrecursion/blacktide/compiler/passes/invariants.clj
index f6092ae..43d29ff 100644
--- a/src/com/tailrecursion/blacktide/compiler/invariants.clj
+++ b/src/com/tailrecursion/blacktide/compiler/passes/invariants.clj
@@ -1,12 +1,12 @@
-(ns ^:typed.clojure com.tailrecursion.blacktide.compiler.invariants
+(ns ^:typed.clojure com.tailrecursion.blacktide.compiler.passes.invariants
   "Remove loop parameters that only forward an outer lexical value. Their
   initializers remain in place, and recur keeps exactly the retained parameters."
   (:require [clojure.set :as set]
             [com.tailrecursion.nanopass :as np]
-            [com.tailrecursion.blacktide.compiler.structured-languages :as s]
+            [com.tailrecursion.blacktide.compiler.languages :as l]
             [typed.clojure :as-alias t]))
 
-(np/defpass assigned {:from [s/Joined Expr] :to (t/Set t/Sym)}
+(np/defpass assigned {:from [l/Joined Expr] :to (t/Set t/Sym)}
   [node]
   :bind (conj (assigned (:body node)) (:target (:binding node)))
   :join (conj (set/union (assigned (:value node)) (assigned (:body node))) (:target node))
@@ -15,7 +15,7 @@
   :loop (into (assigned (:body node)) (:slots node))
   :recur #{} :return #{} :throw #{})
 
-(np/defpass expression {:from [s/Joined Expr] :to [s/Joined Expr] :args [(t/Set t/Sym)]}
+(np/defpass expression {:from [l/Joined Expr] :to [l/Joined Expr] :args [(t/Set t/Sym)]}
   [invariants node]
   :bind (assoc node :body (expression invariants (:body node)))
   :join (assoc node :value (expression invariants (:value node)) :body (expression invariants (:body node)))
diff --git a/src/com/tailrecursion/blacktide/compiler/joins.clj b/src/com/tailrecursion/blacktide/compiler/passes/joins.clj
similarity index 80%
rename from src/com/tailrecursion/blacktide/compiler/joins.clj
rename to src/com/tailrecursion/blacktide/compiler/passes/joins.clj
index 141cc29..fd70633 100644
--- a/src/com/tailrecursion/blacktide/compiler/joins.clj
+++ b/src/com/tailrecursion/blacktide/compiler/passes/joins.clj
@@ -1,20 +1,19 @@
-(ns ^:typed.clojure com.tailrecursion.blacktide.compiler.joins
+(ns ^:typed.clojure com.tailrecursion.blacktide.compiler.passes.joins
   "Share acyclic branch continuations with at most one escaping definition.
   Every incoming path must define the result. Postdominators nominate exits;
   region proofs reject cycles and incomplete paths before recovery."
   (:require [clojure.set :as set]
             [com.tailrecursion.nanopass :as np]
             [com.tailrecursion.blacktide.compiler.languages :as l]
-            [com.tailrecursion.blacktide.compiler.analysis-languages :as a]
-            [com.tailrecursion.blacktide.compiler.graph :as graph]
+            [com.tailrecursion.blacktide.compiler.passes.graph :as graph]
             [typed.clojure :as-alias t]))
 
-(np/defpass postdominators {:from [l/Machine Program] :to [a/Planned Postdominators] :body true}
+(np/defpass postdominators {:from [l/Machine Program] :to [l/Planned Postdominators] :body true}
   [machine]
   (let [labels (set (keys (:blocks machine)))]
-    ((fn ^{::t/- [a/Planned-Postdominators :-> a/Planned-Postdominators]} solve [known]
+    ((fn ^{::t/- [l/Planned-Postdominators :-> l/Planned-Postdominators]} solve [known]
        (let [next (reduce-kv
-                    (fn [^{::t/- a/Planned-Postdominators} out ^{::t/- l/Machine-Label} id
+                    (fn [^{::t/- l/Planned-Postdominators} out ^{::t/- l/Machine-Label} id
                          ^{::t/- l/Machine-Block} block]
                       (let [successors (graph/successors (:term block))
                             common (if (seq successors)
@@ -23,14 +22,14 @@
          (if (= known next) known (recur next)))) (zipmap labels (repeat labels)))))
 
 (np/defpass region
-  {:from [l/Machine Label] :to (t/Option a/Planned-Region)
+  {:from [l/Machine Label] :to (t/Option l/Planned-Region)
    :args [l/Machine-Program l/Machine-Label] :body true}
   [machine stop id]
   ;; Prove blocks only after every successor is proved. Each block contributes
   ;; once; shared paths do not exhaust a traversal budget. Cycles never qualify.
-  ((fn ^{::t/- [a/Planned-Regions :-> (t/Option a/Planned-Region)]} solve [known]
+  ((fn ^{::t/- [l/Planned-Regions :-> (t/Option l/Planned-Region)]} solve [known]
      (let [next (reduce-kv
-                  (fn [^{::t/- a/Planned-Regions} out ^{::t/- l/Machine-Label} label
+                  (fn [^{::t/- l/Planned-Regions} out ^{::t/- l/Machine-Label} label
                        ^{::t/- l/Machine-Block} block]
                     (let [successors (graph/successors (:term block))]
                       (if (and (not (contains? out label)) (seq successors)
@@ -44,8 +43,8 @@
          (when (not= known next) (recur next))))) {stop {:definite #{} :changed #{}}}))
 
 (np/defpass candidate
-  {:from [l/Machine Label] :to (t/Option a/Planned-Join)
-   :args [l/Machine-Program a/Planned-Postdominators] :body true}
+  {:from [l/Machine Label] :to (t/Option l/Planned-Join)
+   :args [l/Machine-Program l/Planned-Postdominators] :body true}
   [machine known id]
   (when-let [block (get (:blocks machine) id)]
     (let [term (:term block)
@@ -62,10 +61,10 @@
                 (when (and (<= (count outputs) 1) (symbol? result) (contains? (:definite proof) result))
                   {:target target :result result})))))))))
 
-(np/defpass plan {:from [a/Analysis Program] :to [a/Planned Program] :body true}
+(np/defpass plan {:from [l/Analysis Program] :to [l/Planned Program] :body true}
   [analysis]
   (let [machine (:machine analysis) known (postdominators machine)]
     (assoc analysis :joins
-      (reduce (fn [^{::t/- a/Planned-Joins} out ^{::t/- l/Machine-Label} id]
+      (reduce (fn [^{::t/- l/Planned-Joins} out ^{::t/- l/Machine-Label} id]
                 (if-let [join (candidate machine known id)] (assoc out id join) out))
               {} (keys (:blocks machine))))))
diff --git a/src/com/tailrecursion/blacktide/compiler/loop_facts.clj b/src/com/tailrecursion/blacktide/compiler/passes/loop_facts.clj
similarity index 51%
rename from src/com/tailrecursion/blacktide/compiler/loop_facts.clj
rename to src/com/tailrecursion/blacktide/compiler/passes/loop_facts.clj
index 74c805f..567a5ea 100644
--- a/src/com/tailrecursion/blacktide/compiler/loop_facts.clj
+++ b/src/com/tailrecursion/blacktide/compiler/passes/loop_facts.clj
@@ -1,16 +1,15 @@
-(ns ^:typed.clojure com.tailrecursion.blacktide.compiler.loop-facts
+(ns ^:typed.clojure com.tailrecursion.blacktide.compiler.passes.loop-facts
   "Recover source loop representations using Clojure's monotone boxing analysis.
   Reanalyze expanded bindings in order without repeating user macros. Native
   syntax retains its initializers; lowered loops materialize the conversions."
   (:require [com.tailrecursion.nanopass :as np]
-            [com.tailrecursion.blacktide.compiler.source-languages :as s]
-            [com.tailrecursion.blacktide.compiler.jvm-languages :as j]
+            [com.tailrecursion.blacktide.compiler.languages :as l]
             [com.tailrecursion.blacktide.compiler.jvm :as jvm]
             [com.tailrecursion.blacktide.compiler.errors :as errors]
-            [com.tailrecursion.blacktide.compiler.source-jvm :as facts]
+            [com.tailrecursion.blacktide.compiler.passes.source-jvm :as facts]
             [typed.clojure :as-alias t]))
 
-(np/defpass children {:from [s/Bound Expr] :to [s/Bound Exprs] :body true}
+(np/defpass children {:from [l/Bound Expr] :to [l/Bound Exprs] :body true}
   [expr]
   (let [node (:node expr)]
     (case (:op node)
@@ -25,119 +24,119 @@
       (:setq :psetq) (mapv :value (:pairs node))
       [])))
 
-
-(np/defpass primitive {:from [j/JVM Fact] :to (t/Option java.lang.Class) :body true}
+(np/defpass primitive {:from [l/JVM Fact] :to (t/Option java.lang.Class) :body true}
   [fact]
   (let [type (:type fact)]
     (when (and (:primitive? fact) (class? type) (.isPrimitive ^Class type) (not= Void/TYPE type)) type)))
 
-(np/defpass inferred {:from [s/Bound Expr] :to [s/Bound Expr] :args [s/Bound-Node] :body true}
+(np/defpass inferred {:from [l/Bound Expr] :to [l/Bound Expr] :args [l/Bound-Node] :body true}
   [node expr]
   (assoc expr :node node :jvm (facts/infer {:hint (:hint (:jvm expr)) :var-meta (:var-meta (:jvm expr))} node)))
 
-(np/defpass converted {:from [s/Bound Expr] :to [s/Bound Expr] :args [java.lang.Class] :body true}
+(np/defpass converted {:from [l/Bound Expr] :to [l/Bound Expr] :args [java.lang.Class] :body true}
   [^Class type expr]
   (let [node {:op :host :operation {:op :static :class clojure.lang.RT
                                    :member (symbol (str (.getName type) "Cast")) :tags [type]}
              :args [expr]}]
     (assoc expr :node node :jvm (facts/infer {:hint nil :var-meta {}} node))))
 
-(np/defpass boxed {:from [s/Bound Expr] :to [s/Bound Expr] :body true}
+(np/defpass boxed {:from [l/Bound Expr] :to [l/Bound Expr] :body true}
   [expr]
   (let [node {:op :host :operation {:op :static :class clojure.lang.RT :member 'box :tags nil} :args [expr]}]
     ;; Reanalysis can select RT.box(Object), rather than RT.box(long/double).
     (assoc expr :node node :jvm (facts/infer {:hint nil :var-meta {}} node))))
 
-(np/defpass initializer {:from [s/Bound Expr] :to [s/Bound Expr] :args [t/Bool] :body true}
+(np/defpass initializer {:from [l/Bound Expr] :to [l/Bound Expr] :args [t/Bool] :body true}
   [box? init]
   (let [type (primitive (:jvm init))]
     (if box? (boxed init)
       (if-let [promoted (cond (= type Integer/TYPE) Long/TYPE (= type Float/TYPE) Double/TYPE)]
         (converted promoted init) init))))
 
-(declare expression solve)
-(np/defpass bindings
-  {:from [s/Bound Bindings] :to (t/HVec [(t/Map t/Sym s/Bound-Local) s/Bound-Bindings])
-   :args [(t/Map t/Sym s/Bound-Local) t/Bool (t/Set t/Sym)] :body true}
-  [env loop? boxed-ids originals]
-  (reduce (fn [^{::t/- (t/HVec [(t/Map t/Sym s/Bound-Local) s/Bound-Bindings])} state
-               ^{::t/- s/Bound-Binding} binding]
-            (let [[env out] state init (expression env (:init binding)) local (:local binding)
-                  effective (if loop? (initializer (contains? boxed-ids (:id local)) init) init)
-                  value (facts/binding-facts (:name local) (:mutable? local) (:jvm effective))
-                  local (assoc local :jvm value)]
-              [(assoc env (:id local) local) (conj out {:local local :init init})])) [env []] originals))
-
-(np/defpass expression
-  {:from [s/Bound Expr] :to [s/Bound Expr] :args [(t/Map t/Sym s/Bound-Local)] :body true}
-  [env expr]
-  (let [node (:node expr)
-        node (case (:op node)
-               :local (assoc node :local (get env (:id (:local node)) (:local node)))
-               :if (assoc node :test (expression env (:test node))
-                               :then (expression env (:then node)) :else (expression env (:else node)))
-               :case (assoc node :selector (expression env (:selector node))
-                                 :arms (mapv #(expression env %) (:arms node)))
-               (:dispatch :throw) (assoc node :value (expression env (:value node)))
-               :do (assoc node :forms (mapv #(expression env %) (:forms node)))
-               :collection (assoc node :items (mapv #(expression env %) (:items node))
-                                       :metadata (some->> (:metadata node) (expression env)))
-               :host (assoc node :args (mapv #(expression env %) (:args node)))
-               :recur (assoc node :args (mapv #(expression env %) (:args node))
-                                  :target (assoc (:target node) :bindings
-                                            (mapv #(get env (:id %) %) (:bindings (:target node)))))
-               :invoke (assoc node :function (expression env (:function node))
-                                   :args (mapv #(expression env %) (:args node)))
-               :let (let [[inner values] (bindings env false #{} (:bindings node))]
-                                (assoc node :bindings values :body (expression inner (:body node))))
-               :loop (:node (first (solve env expr)))
-               (:setq :psetq) (assoc node :pairs (mapv #(assoc % :value (expression env (:value %))) (:pairs node)))
-               node)]
-    (inferred node expr)))
-
-(np/defpass recurs {:from [s/Bound Expr] :to [s/Bound Exprs] :args [t/Sym] :body true}
+(np/defpass recurs {:from [l/Bound Expr] :to [l/Bound Exprs] :args [t/Sym] :body true}
   [target expr]
   (if (and (= :recur (:op (:node expr))) (= target (:id (:target (:node expr)))))
     [expr] (into [] (mapcat #(recurs target %)) (children expr))))
 
-(np/defpass compatible? {:from [j/JVM Fact] :to t/Bool :args [java.lang.Class] :body true}
+(np/defpass compatible? {:from [l/JVM Fact] :to t/Bool :args [java.lang.Class] :body true}
   [type fact]
   (let [actual (primitive fact)]
     (or (= type actual) (and (= type Long/TYPE) (contains? #{Integer/TYPE Short/TYPE Byte/TYPE Character/TYPE} actual))
         (and (= type Double/TYPE) (= actual Float/TYPE)))))
 
-(np/defpass solve
-  {:from [s/Bound Expr] :to (t/HVec [s/Bound-Expr (t/Set t/Sym)])
-   :args [(t/Map t/Sym s/Bound-Local)] :body true}
-  [env expr]
-  (let [node (:node expr)]
-    (if-not (= :loop (:op node)) (throw (IllegalStateException. "Expected source loop"))
-      ((fn ^{::t/- [(t/Set t/Sym) :-> (t/HVec [s/Bound-Expr (t/Set t/Sym)])]} fixed [boxed-ids]
-         (let [[env values] (bindings env true boxed-ids (:bindings node))
-               body (expression env (:body node))
-               mismatches (reduce
-                            (fn [^{::t/- (t/Set t/Sym)} out ^{::t/- s/Bound-Expr} edge]
-                              (into out (keep identity)
-                                (map (fn [^{::t/- s/Bound-Binding} binding ^{::t/- s/Bound-Expr} arg]
-                                       (let [local (:local binding) type (primitive (:jvm local))]
-                                         (when (and type (contains? #{Long/TYPE Double/TYPE} type)
-                                                    (not (compatible? type (:jvm arg)))) (:id local))))
-                                     values (:args (:node edge))))) boxed-ids (recurs (:id node) body))]
-           (if (not= boxed-ids mismatches) (recur mismatches)
-             [(inferred (assoc node :bindings values :body body) expr) boxed-ids]))) #{}))))
+(np/defpasses
+  (bindings
+    {:from [l/Bound Bindings] :to (t/HVec [(t/Map t/Sym l/Bound-Local) l/Bound-Bindings])
+     :args [(t/Map t/Sym l/Bound-Local) t/Bool (t/Set t/Sym)] :body true}
+    [env loop? boxed-ids originals]
+    (reduce (fn [^{::t/- (t/HVec [(t/Map t/Sym l/Bound-Local) l/Bound-Bindings])} state
+                 ^{::t/- l/Bound-Binding} binding]
+              (let [[env out] state init (expression env (:init binding)) local (:local binding)
+                    effective (if loop? (initializer (contains? boxed-ids (:id local)) init) init)
+                    value (facts/binding-facts (:name local) (:mutable? local) (:jvm effective))
+                    local (assoc local :jvm value)]
+                [(assoc env (:id local) local) (conj out {:local local :init init})])) [env []] originals))
+
+  (expression
+    {:from [l/Bound Expr] :to [l/Bound Expr] :args [(t/Map t/Sym l/Bound-Local)] :body true}
+    [env expr]
+    (let [node (:node expr)
+          node (case (:op node)
+                 :local (assoc node :local (get env (:id (:local node)) (:local node)))
+                 :if (assoc node :test (expression env (:test node))
+                                 :then (expression env (:then node)) :else (expression env (:else node)))
+                 :case (assoc node :selector (expression env (:selector node))
+                                   :arms (mapv #(expression env %) (:arms node)))
+                 (:dispatch :throw) (assoc node :value (expression env (:value node)))
+                 :do (assoc node :forms (mapv #(expression env %) (:forms node)))
+                 :collection (assoc node :items (mapv #(expression env %) (:items node))
+                                         :metadata (some->> (:metadata node) (expression env)))
+                 :host (assoc node :args (mapv #(expression env %) (:args node)))
+                 :recur (assoc node :args (mapv #(expression env %) (:args node))
+                                    :target (assoc (:target node) :bindings
+                                              (mapv #(get env (:id %) %) (:bindings (:target node)))))
+                 :invoke (assoc node :function (expression env (:function node))
+                                     :args (mapv #(expression env %) (:args node)))
+                 :let (let [[inner values] (bindings env false #{} (:bindings node))]
+                                  (assoc node :bindings values :body (expression inner (:body node))))
+                 :loop (:node (first (solve env expr)))
+                 (:setq :psetq) (assoc node :pairs (mapv #(assoc % :value (expression env (:value %))) (:pairs node)))
+                 node)]
+      (inferred node expr)))
+
+  (solve
+    {:from [l/Bound Expr] :to (t/HVec [l/Bound-Expr (t/Set t/Sym)])
+     :args [(t/Map t/Sym l/Bound-Local)] :body true}
+    [env expr]
+    (let [node (:node expr)]
+      (if-not (= :loop (:op node)) (throw (IllegalStateException. "Expected source loop"))
+        ((fn ^{::t/- [(t/Set t/Sym) :-> (t/HVec [l/Bound-Expr (t/Set t/Sym)])]} fixed [boxed-ids]
+           (let [[env values] (bindings env true boxed-ids (:bindings node))
+                 body (expression env (:body node))
+                 mismatches (reduce
+                              (fn [^{::t/- (t/Set t/Sym)} out ^{::t/- l/Bound-Expr} edge]
+                                (into out (keep identity)
+                                  (map (fn [^{::t/- l/Bound-Binding} binding ^{::t/- l/Bound-Expr} arg]
+                                         (let [local (:local binding) type (primitive (:jvm local))]
+                                           (when (and type (contains? #{Long/TYPE Double/TYPE} type)
+                                                      (not (compatible? type (:jvm arg)))) (:id local))))
+                                       values (:args (:node edge))))) boxed-ids (recurs (:id node) body))]
+             (if (not= boxed-ids mismatches) (recur mismatches)
+               [(inferred (assoc node :bindings values :body body) expr) boxed-ids]))) #{})))))
 
 ;; RecurExpr's parser admits byte/short/char to long, but its emitter rejects
 ;; them. Preserve that validation before replacing recur with graph edges.
+
 (np/defpass recur-error
-  {:from [s/Bound Expr] :to (t/Option (t/HVec [s/Bound-Local s/Bound-Expr])) :body true}
+  {:from [l/Bound Expr] :to (t/Option (t/HVec [l/Bound-Local l/Bound-Expr])) :body true}
   [expr]
   (let [node (:node expr)]
     (when (= :loop (:op node))
       (first
-        (keep (fn [^{::t/- s/Bound-Expr} edge]
+        (keep (fn [^{::t/- l/Bound-Expr} edge]
                 (first
                   (keep identity
-                    (map (fn [^{::t/- s/Bound-Binding} binding ^{::t/- s/Bound-Expr} arg]
+                    (map (fn [^{::t/- l/Bound-Binding} binding ^{::t/- l/Bound-Expr} arg]
                            (let [local (:local binding) type (primitive (:jvm local)) actual (primitive (:jvm arg))]
                              (when (and type (not (or (= type actual)
                                                      (and (= type Long/TYPE) (= actual Integer/TYPE))
@@ -145,7 +144,7 @@
                                [local arg]))) (:bindings node) (:args (:node edge))))))
               (recurs (:id node) (:body node)))))))
 
-(np/defpass recurrence {:from [s/Bound Expr] :to [s/Bound Expr] :body true}
+(np/defpass recurrence {:from [l/Bound Expr] :to [l/Bound Expr] :body true}
   [expr]
   (let [node (:node expr)]
     (case (:op node)
@@ -161,7 +160,7 @@
       :let (assoc expr :node (assoc node :body (recurrence (:body node))))
       expr)))
 
-(np/defpass materialize {:from [s/Bound Expr] :to [s/Bound Expr] :args [(t/Set t/Sym)] :body true}
+(np/defpass materialize {:from [l/Bound Expr] :to [l/Bound Expr] :args [(t/Set t/Sym)] :body true}
   [boxed-ids expr]
   (let [node (:node expr)]
     (if-not (= :loop (:op node)) (throw (IllegalStateException. "Expected source loop"))
@@ -177,7 +176,7 @@
                         init (if hint (inferred {:op :host :operation {:op :adapt :class hint} :args [init]} init) init)]
                     (assoc binding :init init))) (:bindings node)))))))
 
-(np/defpass lower {:from [s/Bound Expr] :to [s/Bound Expr] :body true}
+(np/defpass lower {:from [l/Bound Expr] :to [l/Bound Expr] :body true}
   [expr]
   (let [[expr boxed-ids] (solve {} expr)]
     (when-let [[local arg] (recur-error expr)]
diff --git a/src/com/tailrecursion/blacktide/compiler/passes/lower.clj b/src/com/tailrecursion/blacktide/compiler/passes/lower.clj
new file mode 100644
index 0000000..b170b65
--- /dev/null
+++ b/src/com/tailrecursion/blacktide/compiler/passes/lower.clj
@@ -0,0 +1,139 @@
+(ns ^:typed.clojure com.tailrecursion.blacktide.compiler.passes.lower
+  "Lower ordered expressions into a CFG with explicit branch joins and exits.
+  Operands have explicit bindings; operations retain their execution position."
+  (:require [com.tailrecursion.blacktide.compiler.errors :as errors]
+            [com.tailrecursion.nanopass :as np]
+            [com.tailrecursion.blacktide.compiler.languages :as l]
+            [com.tailrecursion.blacktide.compiler.passes.source :as source]
+            [typed.clojure :as-alias t]))
+
+(np/defpass fresh-block
+  {:from [l/Symbolic Builder] :to (t/HVec [l/Symbolic-Builder l/Symbolic-Label]) :body true}
+  [builder]
+  [(assoc builder :next (inc (:next builder))) (:next builder)])
+
+(np/defpass start
+  {:from [l/Symbolic Label] :to [l/Symbolic Builder] :args [l/Symbolic-Builder] :body true}
+  [builder label]
+  (assoc builder :current label :ops []))
+
+(np/defpass seal
+  {:from [l/Symbolic Term] :to [l/Symbolic Builder] :args [l/Symbolic-Builder] :body true}
+  [builder term]
+  (if (contains? (:blocks builder) (:current builder))
+    (throw (ex-info "Control-flow block already has a terminator" {:kind :invalid-graph :block (:current builder)}))
+    (assoc builder :blocks (assoc (:blocks builder) (:current builder) {:ops (:ops builder) :term term}) :ops [])))
+
+(np/defpass evaluate
+  {:from [l/Symbolic Rhs] :to (t/HVec [l/Symbolic-Builder l/Symbolic-Atom])
+   :args [l/Symbolic-Builder] :body true}
+  [builder rhs]
+  (let [[supply id] (source/fresh "value" (:supply builder))]
+    [(-> builder (assoc :supply supply) (update :ops conj {:target id :value rhs})) {:op :local :id id}]))
+
+(np/defpasses
+  (lower-forms
+    {:from [l/Ordered Exprs] :to (t/HVec [l/Symbolic-Builder (t/Vec l/Symbolic-Atom)])
+     :args [l/Symbolic-Builder] :body true}
+    [builder forms]
+    (reduce (fn [^{::t/- (t/HVec [l/Symbolic-Builder (t/Vec l/Symbolic-Atom)])} acc ^{::t/- l/Ordered-Expr} expr]
+              (let [[builder out] acc [builder value] (lower builder expr)] [builder (conj out value)]))
+            [builder []] forms))
+
+  (lower-bindings
+    {:from [l/Ordered Bindings] :to [l/Symbolic Builder] :args [l/Symbolic-Builder] :body true}
+    [builder bindings]
+    (reduce (fn [^{::t/- l/Symbolic-Builder} builder ^{::t/- l/Ordered-Binding} binding]
+              (let [[builder value] (lower builder (:init binding))]
+                (update builder :ops conj {:target (:id (:local binding)) :value {:op :value :value value}})))
+            builder bindings))
+
+  (lower-node
+    {:from [l/Ordered Node] :to (t/HVec [l/Symbolic-Builder l/Symbolic-Atom])
+     :args [l/Symbolic-Builder l/Ordered-Expr]}
+    [builder expr node]
+    :literal [builder {:op :literal :value (:value node)}]
+    :local (let [local (:local node)]
+             (evaluate builder (if (:external? local) {:op :external :name (:id local) :jvm (:jvm local)}
+                                   {:op :value :value {:op :local :id (:id local)}})))
+    :global (evaluate builder {:op :global :name (:name node)})
+    :var (evaluate builder {:op :var :name (:name node)})
+    :native (evaluate builder node)
+    :dispatch (evaluate builder {:op :dispatch :spec (:spec node)
+                                 :value {:value {:op :local :id (:id (:value node))} :jvm (:jvm (:value node))}})
+    :select
+    (let [[builder test] (lower builder (:selector node))
+          [builder join] (fresh-block builder)
+          [supply result] (source/fresh "case_result" (:supply builder))
+          [builder targets] (reduce (fn [^{::t/- (t/HVec [l/Symbolic-Builder (t/Vec l/Symbolic-Label)])} acc
+                                         ^{::t/- l/Ordered-Expr} _]
+                                      (let [[builder targets] acc [builder label] (fresh-block builder)]
+                                        [builder (conj targets label)]))
+                                    [(assoc builder :supply supply) []] (:arms node))
+          builder (seal builder {:op :switch :test test :targets targets})
+          builder (reduce (fn [^{::t/- l/Symbolic-Builder} builder ^{::t/- t/Int} index]
+                            (let [[builder value] (lower (start builder (nth targets index)) (nth (:arms node) index))]
+                              (seal (update builder :ops conj {:target result :value {:op :value :value value}})
+                                    {:op :jump :target join}))) builder (range (count targets)))]
+      [(start builder join) {:op :local :id result}])
+    :loop (let [builder (lower-bindings builder (:bindings node))
+                [builder header] (fresh-block builder)
+                builder (assoc builder :loops (assoc (:loops builder) (:id node) header))]
+            (lower (start (seal builder {:op :jump :target header}) header) (:body node)))
+    :recur (let [target (:target node)
+                 header (or (get (:loops builder) (:id target))
+                            (errors/fail :invalid-graph "Missing source recur target" {} (:site expr)))
+                 writes (mapv (fn [local arg] {:target (:id local) :value {:op :value :value {:op :local :id (:id arg)}}})
+                              (:bindings target) (:args node))
+                 builder (seal (update builder :ops into writes) {:op :jump :target header})
+                 [builder next] (fresh-block builder)]
+             [(start builder next) {:op :literal :value nil}])
+    :if (let [[builder test] (lower builder (:test node))
+              [builder yes] (fresh-block builder) [builder no] (fresh-block builder) [builder join] (fresh-block builder)
+              [supply result] (source/fresh "join" (:supply builder))
+              builder (seal (assoc builder :supply supply) {:op :branch :test test :yes yes :no no})
+              [builder a] (lower (start builder yes) (:then node))
+              builder (seal (update builder :ops conj {:target result :value {:op :value :value a}}) {:op :jump :target join})
+              [builder b] (lower (start builder no) (:else node))
+              builder (seal (update builder :ops conj {:target result :value {:op :value :value b}}) {:op :jump :target join})]
+          [(start builder join) {:op :local :id result}])
+    :throw (let [[builder value] (lower builder (:value node))
+                 builder (seal builder {:op :throw :value value})
+                 [builder next] (fresh-block builder)]
+             [(start builder next) {:op :literal :value nil}])
+    :write (let [[builder value] (lower builder (:value node))]
+             [(update builder :ops conj {:target (:id (:target node)) :value {:op :value :value value}}) value])
+    :convert (evaluate builder {:op :convert :conversion (:conversion node)
+                                :value {:value {:op :local :id (:id (:value node))} :jvm (:jvm (:value node))}})
+    :host-call (evaluate builder {:op :host :operation (:operation node)
+                             :args (mapv (fn [local] {:value {:op :local :id (:id local)} :jvm (:jvm local)}) (:args node))})
+    :invoke (evaluate builder {:op :invoke :function {:op :local :id (:id (:function node))}
+                               :args (mapv (fn [local] {:op :local :id (:id local)}) (:args node))})
+    :build (evaluate builder {:op :construct :kind (:kind node) :unique? (:unique? node)
+                              :items (mapv (fn [local] {:op :local :id (:id local)}) (:items node))})
+    :metadata (evaluate builder {:op :metadata :value {:op :local :id (:id (:value node))}
+                                 :metadata {:op :local :id (:id (:metadata node))}})
+    :label (let [key (:name node)]
+             (when (contains? (:labels builder) key)
+               (errors/fail :duplicate-label "Duplicate label" {:label (:name node)} (:site expr)))
+             (let [[builder label] (fresh-block builder)
+                   builder (assoc builder :labels (assoc (:labels builder) key label))
+                   builder (seal builder {:op :jump :target label})]
+               [(start builder label) {:op :literal :value nil}]))
+    :goto (let [builder (seal builder {:op :transfer :name (:name node) :site (:site expr)})
+                [builder next] (fresh-block builder)]
+            [(start builder next) {:op :literal :value nil}])
+    :do (let [[builder values] (lower-forms builder (:forms node))]
+          [builder (or (peek values) {:op :literal :value nil})])
+    :let (lower (lower-bindings builder (:bindings node)) (:body node)))
+
+  (lower
+    {:from [l/Ordered Expr] :to (t/HVec [l/Symbolic-Builder l/Symbolic-Atom]) :args [l/Symbolic-Builder] :body true}
+    [builder expr]
+    (lower-node builder expr (:node expr))))
+
+(np/defpass compile-graph
+  {:from [l/Ordered Expr] :to [l/Symbolic Program] :args [l/Bound-Supply] :body true}
+  [supply expr]
+  (let [[builder value] (lower {:supply supply :ops [] :next 1 :current 0 :blocks {} :labels {} :loops {}} expr)]
+    {:entry 0 :labels (:labels builder) :blocks (:blocks (seal builder {:op :return :value value}))}))
diff --git a/src/com/tailrecursion/blacktide/compiler/passes/native.clj b/src/com/tailrecursion/blacktide/compiler/passes/native.clj
new file mode 100644
index 0000000..08c5c00
--- /dev/null
+++ b/src/com/tailrecursion/blacktide/compiler/passes/native.clj
@@ -0,0 +1,104 @@
+(ns ^:typed.clojure com.tailrecursion.blacktide.compiler.passes.native
+  "Analyze native free reads and recover their source types from machine storage.
+  Primitive source reads cannot observe the slot's box; reference reads retain
+  it. Internal bindings keep native inference."
+  (:require [clojure.set :as set]
+            [com.tailrecursion.nanopass :as np]
+            [com.tailrecursion.blacktide.compiler.languages :as l]
+            [com.tailrecursion.blacktide.compiler.passes.collections :as collections]
+            [com.tailrecursion.blacktide.compiler.passes.host-emit :as host]
+            [com.tailrecursion.blacktide.compiler.passes.case-emit :as cases]
+            [typed.clojure :as-alias t]))
+
+(np/defpass literal-values {:from [l/Native Exprs] :to (t/Option (t/Vec t/Any)) :body true}
+  [items]
+  (reduce (fn [^{::t/- (t/Option (t/Vec t/Any))} values ^{::t/- l/Native-Expr} expr]
+            (let [node (:node expr)]
+              (if (and values (= :literal (:op node))) (conj values (:value node)) (reduced nil)))) [] items))
+
+(np/defpass hinted-form {:from [l/Machine Code] :to [l/Machine Code] :args [l/JVM-Raw] :body true}
+  [hint code]
+  ;; Var return tags may be Class objects; source form tags cannot be.
+  (let [hint (if (class? hint) (symbol (.getName ^Class hint)) hint)]
+    (with-meta (if (symbol? code) code (apply list code))
+               (cond-> (meta code) hint (assoc :tag hint)))))
+
+(np/defpasses
+  (bindings-form
+    {:from [l/Native Bindings] :to (t/Vec t/Any) :args [(t/Set t/Sym)] :body true}
+    [bound bindings]
+    (vec (mapcat (fn [^{::t/- l/Native-Binding} binding]
+                   [(with-meta (:id (:local binding)) (meta (:name (:local binding))))
+                    (form bound (:init binding))]) bindings)))
+
+  (node-form
+    {:from [l/Native Node] :to [l/Machine Code] :args [(t/Set t/Sym) l/Native-Expr]}
+    [bound expr node]
+    :literal (list 'quote (:value node))
+    :global (host/typed-form (:jvm expr) (host/global-form (:name node)))
+    :var (list 'var (:name node))
+    :local (let [local (:local node) id (:id local)]
+             (if (contains? bound id)
+               (with-meta id (when-let [hint (:hint (:jvm expr))] {:tag hint}))
+               (host/typed-form (:jvm expr) (host/box-form id))))
+    :host (hinted-form (:hint (:jvm expr)) (host/host-form (mapv #(form bound %) (:args node)) (:operation node)))
+    :invoke (let [function (:function node) source (:form (:site function))
+                  ;; Quote changes KeywordExpr into ConstantExpr, bypassing the
+                  ;; source call site's IKeywordLookup cache. Preserve its syntax.
+                  callee (if (and (keyword? source) (= :literal (:op (:node function))))
+                           source (form bound function))]
+              (hinted-form (:hint (:jvm expr)) (list* callee (mapv #(form bound %) (:args node)))))
+    :if (list 'if (form bound (:test node)) (form bound (:then node)) (form bound (:else node)))
+    :case (cases/arms-form (mapv #(form bound %) (:arms node)) (form bound (:selector node)))
+    :dispatch (cases/selector-form (form bound (:value node)) (:spec node))
+    :throw (list 'throw (form bound (:value node)))
+    :do (list* 'do (mapv #(form bound %) (:forms node)))
+    :recur (list* 'recur (mapv #(form bound %) (:args node)))
+    :let (let [bound (into bound (map #(:id (:local %))) (:bindings node))]
+           (list 'let* (bindings-form bound (:bindings node)) (form bound (:body node))))
+    :loop (let [bound (into bound (map #(:id (:local %))) (:bindings node))]
+            (list 'loop* (bindings-form bound (:bindings node)) (form bound (:body node))))
+    :collection
+    (let [items (:items node) metadata (:metadata node) values (literal-values items)
+          keys (when (= :map (:kind node)) (literal-values (vec (take-nth 2 items))))
+          unique? (boolean (and keys (= (count keys) (count (set keys)))))
+          _ (when (and (nil? metadata) keys (not unique?))
+              (throw (IllegalArgumentException. "Duplicate constant keys in map")))
+          value (if (and (nil? metadata) values)
+                  (list 'quote (collections/constant values (:kind node)))
+                  (collections/form (mapv #(form bound %) items) unique? (:kind node)))]
+      (if metadata (list '. value 'withMeta (form bound metadata)) value)))
+
+  (form {:from [l/Native Expr] :to [l/Machine Code] :args [(t/Set t/Sym)] :body true}
+    [bound expr] (node-form bound expr (:node expr))))
+
+(np/defpasses
+  (reads-node {:from [l/Native Node] :to (t/Set t/Sym)
+                          :args [(t/Map t/Sym java.lang.Class)]}
+    [types node]
+    :literal #{} :global #{} :var #{}
+    :local (if (:external? (:local node)) #{} #{(:id (:local node))})
+    :host (into #{} (mapcat (partial boxed-reads types)) (:args node))
+    :invoke (into (boxed-reads types (:function node)) (mapcat (partial boxed-reads types)) (:args node))
+    :if (set/union (boxed-reads types (:test node)) (boxed-reads types (:then node)) (boxed-reads types (:else node)))
+    :case (into (boxed-reads types (:selector node)) (mapcat (partial boxed-reads types)) (:arms node))
+    :dispatch (boxed-reads types (:value node))
+    :throw (boxed-reads types (:value node))
+    :collection (into (if-let [metadata (:metadata node)] (boxed-reads types metadata) #{}) (mapcat (partial boxed-reads types)) (:items node))
+    :do (into #{} (mapcat (partial boxed-reads types)) (:forms node))
+    :recur (into #{} (mapcat (partial boxed-reads types)) (:args node))
+    :let (set/difference (into (boxed-reads types (:body node)) (mapcat #(boxed-reads types (:init %))) (:bindings node))
+                         (set (map #(get-in % [:local :id]) (:bindings node))))
+    :loop (set/difference (into (boxed-reads types (:body node)) (mapcat #(boxed-reads types (:init %))) (:bindings node))
+                          (set (map #(get-in % [:local :id]) (:bindings node)))))
+
+  (boxed-reads {:from [l/Native Expr] :to (t/Set t/Sym)
+                           :args [(t/Map t/Sym java.lang.Class)] :body true}
+    [types expr]
+    (let [node (:node expr) fact (:jvm expr)]
+      (if (and (= :local (:op node)) (:primitive? fact)
+               (= (:type fact) (get types (:id (:local node)))))
+        #{} (reads-node types node)))))
+
+(np/defpass reads {:from [l/Native Expr] :to (t/Set t/Sym) :body true}
+  [expr] (boxed-reads {} expr))
diff --git a/src/com/tailrecursion/blacktide/compiler/passes/order.clj b/src/com/tailrecursion/blacktide/compiler/passes/order.clj
new file mode 100644
index 0000000..1d9d83c
--- /dev/null
+++ b/src/com/tailrecursion/blacktide/compiler/passes/order.clj
@@ -0,0 +1,115 @@
+(ns ^:typed.clojure com.tailrecursion.blacktide.compiler.passes.order
+  "Make operand evaluation explicit before CFG construction. Calls and collection
+  construction consume immutable saved locals; source scopes stay unchanged."
+  (:require [com.tailrecursion.nanopass :as np]
+            [com.tailrecursion.blacktide.compiler.languages :as l]
+            [com.tailrecursion.blacktide.compiler.passes.source :as source]
+            [typed.clojure :as-alias t]))
+
+(np/defpass save
+  {:from [l/Ordered Expr] :to (t/HVec [l/Bound-Supply l/Ordered-Binding l/Ordered-Local])
+   :args [l/Bound-Supply] :body true}
+  [supply expr]
+  (let [[supply id] (source/fresh "operand" supply)
+        local {:id id :name id :mutable? false :external? false :jvm (:jvm expr)}]
+    [supply {:local local :init expr} local]))
+
+(np/defpasses
+  (operands
+    {:from [l/Prepared Exprs]
+     :to (t/HVec [l/Bound-Supply (t/Vec l/Ordered-Binding) (t/Vec l/Ordered-Local)])
+     :args [l/Bound-Supply] :body true}
+    [supply exprs]
+    (reduce (fn [^{::t/- (t/HVec [l/Bound-Supply (t/Vec l/Ordered-Binding) (t/Vec l/Ordered-Local)])} acc
+                 ^{::t/- l/Prepared-Expr} expr]
+              (let [[supply bindings locals] acc
+                    [supply expr] (normalize supply expr)
+                    [supply binding local] (save supply expr)]
+                [supply (conj bindings binding) (conj locals local)])) [supply [] []] exprs))
+
+  (normalize-forms
+    {:from [l/Prepared Exprs] :to (t/HVec [l/Bound-Supply l/Ordered-Exprs])
+     :args [l/Bound-Supply] :body true}
+    [supply exprs]
+    (reduce (fn [^{::t/- (t/HVec [l/Bound-Supply l/Ordered-Exprs])} acc ^{::t/- l/Prepared-Expr} expr]
+              (let [[supply out] acc [supply expr] (normalize supply expr)] [supply (conj out expr)]))
+            [supply []] exprs))
+
+  (normalize-bindings
+    {:from [l/Prepared Bindings] :to (t/HVec [l/Bound-Supply l/Ordered-Bindings])
+     :args [l/Bound-Supply] :body true}
+    [supply bindings]
+    (reduce (fn [^{::t/- (t/HVec [l/Bound-Supply l/Ordered-Bindings])} acc ^{::t/- l/Prepared-Binding} binding]
+              (let [[supply out] acc [supply init] (normalize supply (:init binding))]
+                [supply (conj out (assoc binding :init init))])) [supply []] bindings))
+
+  (normalize-node
+    {:from [l/Prepared Node] :to (t/HVec [l/Bound-Supply l/Ordered-Expr])
+     :args [l/Bound-Supply l/Prepared-Expr]}
+    [supply expr node]
+    :literal [supply (assoc expr :node node)]
+    :local [supply (assoc expr :node node)]
+    :global [supply (assoc expr :node node)]
+    :var [supply (assoc expr :node node)]
+    :label [supply (assoc expr :node node)]
+    :goto [supply (assoc expr :node node)]
+    :native [supply (assoc expr :node node)]
+    :select (let [[supply selector] (normalize supply (:selector node))
+                  [supply arms] (normalize-forms supply (:arms node))]
+              [supply (assoc expr :node {:op :select :selector selector :arms arms})])
+    :dispatch (let [[supply value] (normalize supply (:value node))
+                    [supply binding local] (save supply value)]
+                [supply (assoc expr :node {:op :let :bindings [binding]
+                                          :body (assoc expr :node {:op :dispatch :value local :spec (:spec node)})})])
+    :recur (let [[supply bindings args] (operands supply (:args node))]
+             [supply (assoc expr :node {:op :let :bindings bindings
+                                       :body (assoc expr :node {:op :recur :target (:target node) :args args})})])
+    :loop (let [[supply bindings] (normalize-bindings supply (:bindings node))
+                [supply body] (normalize supply (:body node))]
+            [supply (assoc expr :node {:op :loop :id (:id node) :bindings bindings :body body})])
+    :if (let [[supply test] (normalize supply (:test node))
+              [supply yes] (normalize supply (:then node))
+              [supply no] (normalize supply (:else node))]
+          [supply (assoc expr :node {:op :if :test test :then yes :else no})])
+    :throw (let [[supply value] (normalize supply (:value node))]
+             [supply (assoc expr :node {:op :throw :value value})])
+    :write (let [[supply value] (normalize supply (:value node))]
+             [supply (assoc expr :node {:op :write :target (:target node) :value value})])
+    :do (let [[supply forms] (normalize-forms supply (:forms node))]
+          [supply (assoc expr :node {:op :do :forms forms})])
+    :let (let [[supply bindings] (normalize-bindings supply (:bindings node))
+               [supply body] (normalize supply (:body node))]
+           [supply (assoc expr :node {:op :let :bindings bindings :body body})])
+    :convert
+    (let [[supply value] (normalize supply (:value node))
+          [supply binding local] (save supply value)]
+      [supply (assoc expr :node {:op :let :bindings [binding]
+                                :body (assoc expr :node {:op :convert :conversion (:conversion node) :value local})})])
+    :host-call
+    (let [[supply bindings args] (operands supply (:args node))]
+      [supply (assoc expr :node {:op :let :bindings bindings
+                                :body (assoc expr :node {:op :host-call :operation (:operation node) :args args})})])
+    :invoke
+    (let [[supply function] (normalize supply (:function node))
+          [supply binding local] (save supply function)
+          [supply bindings args] (operands supply (:args node))]
+      [supply (assoc expr :node {:op :let :bindings (into [binding] bindings)
+                                :body (assoc expr :node {:op :invoke :function local :args args})})])
+    :build
+    (let [[supply bindings items] (operands supply (:items node))
+          value (assoc expr :node {:op :build :kind (:kind node) :items items :unique? (:unique? node)})
+          [supply bindings value]
+          (if-let [metadata (:metadata node)]
+            (let [[supply binding local] (save supply value)
+                  [supply metadata] (normalize supply metadata)
+                  [supply meta-binding meta-local] (save supply metadata)]
+              [supply (into bindings [binding meta-binding])
+               (assoc expr :node {:op :metadata :value local :metadata meta-local})])
+            [supply bindings value])]
+      [supply (assoc expr :node {:op :let :bindings bindings :body value})]))
+
+  (normalize
+    {:from [l/Prepared Expr] :to (t/HVec [l/Bound-Supply l/Ordered-Expr])
+     :args [l/Bound-Supply] :body true}
+    [supply expr]
+    (normalize-node supply expr (:node expr))))
diff --git a/src/com/tailrecursion/blacktide/compiler/passes/prepare.clj b/src/com/tailrecursion/blacktide/compiler/passes/prepare.clj
new file mode 100644
index 0000000..b524474
--- /dev/null
+++ b/src/com/tailrecursion/blacktide/compiler/passes/prepare.clj
@@ -0,0 +1,44 @@
+(ns ^:typed.clojure com.tailrecursion.blacktide.compiler.passes.prepare
+  "Make function casts and resolved JVM argument conversions explicit before
+  ordering operands. A failing conversion must prevent subsequent evaluation."
+  (:require [com.tailrecursion.nanopass :as np]
+            [com.tailrecursion.blacktide.compiler.languages :as l]
+            [com.tailrecursion.blacktide.compiler.jvm :as jvm]
+            [typed.clojure :as-alias t]))
+
+(np/defpass conversion
+  {:from [l/Prepared Expr] :to [l/Prepared Expr] :args [(t/Option l/JVM-Conversion)] :body true}
+  [conversion expr]
+  (if conversion
+    (let [fact (jvm/fact (.isPrimitive ^Class (:type conversion)) {:hint nil :var-meta {}} (:type conversion))]
+      ;; The consumer still sees Object, but evaluating an Object cast cannot
+      ;; fail. Operand normalization supplies the required value snapshot.
+      (if (= Object (:type conversion)) (assoc expr :jvm fact)
+        (assoc expr :jvm fact :node {:op :convert :conversion conversion :value expr}))) expr))
+
+(np/defpasses
+  (prepare-forms
+    {:from [l/Collections Exprs] :to [l/Prepared Exprs] :processors {Expr prepare}}
+    [forms])
+
+  (prepare-node
+    {:from [l/Collections Node] :to [l/Prepared Node]
+     :processors {Expr prepare Exprs prepare-forms Binding prepare-binding Raw clojure.core/identity Local clojure.core/identity
+                  CollectionKind clojure.core/identity RecurTarget clojure.core/identity CaseSpec clojure.core/identity}}
+    [node]
+    :invoke {:op :invoke :function (conversion {:type clojure.lang.IFn :parameter? false} (prepare (:function node)))
+             :args (mapv prepare (:args node))}
+    :host
+    (let [plan (jvm/prepare-call (mapv #(:type (:jvm %)) (:args node)) (:operation node))]
+      {:op :host-call :operation (:operation plan)
+       :args (mapv (fn [how value] (conversion how (prepare value))) (:conversions plan) (:args node))}))
+
+  (prepare-binding
+    {:from [l/Collections Binding] :to [l/Prepared Binding]
+     :processors {Expr prepare Local clojure.core/identity}}
+    [binding])
+
+  (prepare
+    {:from [l/Collections Expr] :to [l/Prepared Expr] :body true}
+    [expr]
+    (assoc expr :node (prepare-node (:node expr)))))
diff --git a/src/com/tailrecursion/blacktide/compiler/passes/source.clj b/src/com/tailrecursion/blacktide/compiler/passes/source.clj
new file mode 100644
index 0000000..50cbb46
--- /dev/null
+++ b/src/com/tailrecursion/blacktide/compiler/passes/source.clj
@@ -0,0 +1,410 @@
+(ns ^:typed.clojure com.tailrecursion.blacktide.compiler.passes.source
+  "Expand in the caller namespace while discovering lexical bindings. Macro
+  expansion and binding discovery stay ordered; every child becomes bound IR."
+  (:require [com.tailrecursion.blacktide.compiler.errors :as errors]
+            [com.tailrecursion.nanopass :as np]
+            [com.tailrecursion.blacktide.compiler.languages :as l]
+            [typed.clojure :as-alias t]
+            [com.tailrecursion.blacktide.compiler.jvm :as jvm]
+            [com.tailrecursion.blacktide.compiler.passes.source-jvm :as facts]
+            [com.tailrecursion.blacktide.compiler.passes.loop-facts :as loops]
+            [com.tailrecursion.blacktide.compiler.passes.source-case :as cases]
+            [com.tailrecursion.blacktide.compiler.passes.effects :refer [effects]]
+            [com.tailrecursion.blacktide.compiler.passes.source-host :as host]))
+
+(np/defpass fresh
+  {:from [l/Bound Supply] :to (t/HVec [l/Bound-Supply t/Sym]) :args [t/Str] :body true}
+  [stem supply]
+  ;; Put the unique counter before the user-controlled stem. A source name
+  ;; must not impersonate another binding's generated identity.
+  [(assoc supply :next (inc (:next supply))) (symbol (str (:prefix supply) "__" (:next supply) "__" stem))])
+
+(np/defpass expression
+  {:from [l/Bound Node] :to [l/Bound Expr] :args [l/Bound-Context l/JVM-Seed] :body true}
+  [ctx jvm node]
+  {:site (:site ctx) :jvm (facts/infer jvm node) :node node})
+
+(np/defpass resolve-name
+  {:from [l/Bound Raw] :to (t/U nil t/AnyVar java.lang.Class) :args [l/Bound-Context] :body true}
+  [ctx name]
+  (if (var? name) name
+      (when (and (symbol? name) (not (contains? (:locals ctx) name))) (ns-resolve *ns* name))))
+
+(np/defpass parse-symbol
+  {:from [l/Bound Raw] :to [l/Bound Expr] :args [l/Bound-Context] :body true}
+  [ctx form]
+  (if (symbol? form)
+    (if-let [local (get (:locals ctx) form)]
+      (expression ctx {:hint (jvm/source-tag form) :var-meta {}} {:op :local :local local})
+      (let [resolved (resolve-name ctx form) qualified (host/qualified form)]
+        (cond
+          (var? resolved)
+          (let [m (or (meta resolved) {})]
+            (errors/check-visibility resolved (:site ctx))
+            (when (:macro m) (errors/fail :invalid-expression "Cannot take the value of a macro" {} (:site ctx)))
+            (if (:const m)
+              (expression ctx {:hint nil :var-meta {}} {:op :literal :value (.get ^clojure.lang.Var resolved)})
+              (expression ctx {:hint (jvm/source-tag form) :var-meta m}
+                          {:op :global :name (.toSymbol ^clojure.lang.Var resolved)})))
+          (class? resolved) (expression ctx {:hint (jvm/source-tag form) :var-meta {}} {:op :literal :value resolved})
+          qualified
+          (let [operation qualified]
+            (expression ctx {:hint (jvm/source-tag form) :var-meta {}}
+                        {:op :host :operation (if (= :static-field (:op operation)) operation
+                                                 {:op :method-value :name form}) :args []}))
+          :else (errors/fail :unresolved-symbol "Unable to resolve symbol" {:binding form} (:site ctx)))))
+    (errors/fail :invalid-expression "Expected a symbol" {} (:site ctx))))
+
+(np/defpass bind-local
+  {:from [l/Bound Raw] :to (t/HVec [l/Bound-Supply l/Bound-Context l/Bound-Local])
+   :args [l/Bound-Supply l/Bound-Context t/Bool l/JVM-Fact] :body true}
+  [supply ctx mutable? init name]
+  (if (simple-symbol? name)
+    (let [[supply id] (fresh (str name) supply)
+          jvm (facts/binding-facts name mutable? init)
+          type (:type jvm)
+          tag (or (:hint jvm) (when (class? type) (symbol (.getName ^Class type))))
+          local {:id id :name name :mutable? mutable? :external? false
+                 :jvm jvm}]
+      [supply (assoc ctx :locals (assoc (:locals ctx) name local)
+                         :macro-env (assoc (:macro-env ctx) name {:name name :tag tag})) local])
+    (errors/fail :invalid-binding "Binding targets must be simple symbols" {:binding name} (:site ctx))))
+
+(np/defpass case-expression
+  {:from [l/Bound Exprs] :to (t/HVec [l/Bound-Supply l/Bound-Expr])
+   :args [l/Bound-Supply l/Bound-Context l/Bound-Expr] :body true}
+  [supply ctx selector arms]
+  [supply (expression ctx {:hint nil :var-meta {}} {:op :case :selector selector :arms arms})])
+
+(np/defpass protocol-argument
+  {:from [l/Bound Expr] :to (t/HVec [l/Bound-Supply l/Bound-Expr])
+   :args [l/Bound-Supply l/Bound-Context l/Bound-Expr java.lang.Class] :body true}
+  [supply ctx direct? type arg]
+  (let [seed {:hint nil :var-meta {}} [supply id] (fresh "protocol-argument" supply)
+        local {:id id :name id :mutable? false :external? false :jvm (facts/binding-facts id false (:jvm arg))}
+        read (expression ctx seed {:op :local :local local})
+        boxed (expression ctx seed {:op :host :operation {:op :static :class clojure.lang.RT :member 'box :tags [Object]} :args [read]})
+        converted (if (.isPrimitive ^Class type)
+                    (expression ctx seed {:op :host :operation {:op :static :class clojure.lang.RT :member 'box :tags [type]} :args [read]})
+                    (expression ctx seed {:op :host :operation {:op :instance :class Class :member 'cast :mode :method :tags [Object]}
+                                         :args [(expression ctx seed {:op :literal :value type})
+                                                (expression ctx seed {:op :host :operation {:op :adapt :class type} :args [read]})]}))]
+    [supply (expression ctx seed {:op :let :bindings [{:local local :init arg}]
+                                 :body (expression ctx seed {:op :if :test direct? :then converted :else boxed})})]))
+
+(np/defpass protocol-call
+  {:from [l/Bound Exprs] :to (t/HVec [l/Bound-Supply l/Bound-Expr])
+   :args [l/Bound-Supply l/Bound-Context l/JVM-Seed l/JVM-Protocol t/Sym] :body true}
+  [supply ctx seed method name args]
+  (let [plain {:hint nil :var-meta {}} [supply id] (fresh "protocol-receiver" supply)
+        receiver (expression ctx plain {:op :host :operation {:op :static :class clojure.lang.RT :member 'box :tags [Object]} :args [(nth args 0)]})
+        local {:id id :name id :mutable? false :external? false :jvm (:jvm receiver)}
+        read (expression ctx plain {:op :local :local local})
+        direct? (if-let [class (:class method)]
+                  (expression ctx plain {:op :host :operation {:op :instanceof :class class} :args [read]})
+                  (expression ctx plain {:op :literal :value false}))
+        root (expression ctx plain {:op :host :operation {:op :instance :class clojure.lang.Var :member 'getRawRoot :mode :method :tags []}
+                                    :args [(expression ctx plain {:op :var :name name})]})
+        root (expression ctx plain {:op :if :test direct? :then (expression ctx plain {:op :literal :value nil}) :else root})
+        [supply args] (reduce (fn [^{::t/- (t/HVec [l/Bound-Supply l/Bound-Exprs])} acc ^{::t/- t/Int} index]
+                               (let [[supply out] acc arg (nth args index) type (get (:parameters method) (dec index) Object)
+                                     [supply arg] (if (= Object type) [supply arg] (protocol-argument supply ctx direct? type arg))]
+                                 [supply (conj out arg)])) [supply [read root]] (range 1 (count args)))]
+    [supply (expression ctx seed {:op :let :bindings [{:local local :init receiver}]
+                                 :body (expression ctx seed {:op :host :operation {:op :protocol :method method} :args args})})]))
+
+(np/defpass keyword-call
+  {:from [l/Bound Expr] :to (t/HVec [l/Bound-Supply l/Bound-Expr])
+   :args [l/Bound-Supply l/Bound-Context l/JVM-Seed t/Kw] :body true}
+  [supply ctx seed key target]
+  (let [plain {:hint nil :var-meta {}} [supply id] (fresh "keyword-site" supply)
+        site (expression ctx plain {:op :host :operation {:op :keyword-site :key key} :args []})
+        local {:id id :name id :mutable? false :external? false :jvm (:jvm site)}
+        read (expression ctx plain {:op :local :local local})
+        snapshot (expression ctx plain {:op :host :operation {:op :instance :class com.tailrecursion.blacktide.runtime.KeywordSite
+                                                              :member 'snapshot :mode :method :tags []} :args [read]})]
+    ;; The first argument snapshots the cache before the target runs.
+    ;; An IFn call retains KeywordInvokeExpr's unknown result type.
+    [supply (expression ctx seed {:op :let :bindings [{:local local :init site}]
+                                 :body (expression ctx seed {:op :invoke :function read :args [snapshot target]})})]))
+
+(np/defpass parse-marker
+  {:from [l/Bound Forms] :to [l/Bound Expr] :args [l/Bound-Context (t/U ':label ':goto)] :body true}
+  [ctx kind forms]
+  (let [name (second forms)]
+    (when-not (and (= 2 (count forms)) (simple-symbol? name))
+      (errors/fail :invalid-marker "label/goto requires one simple symbol" {:label name} (:site ctx)))
+    (when-not (and (:statement? ctx) (:transfer? ctx) (or (= kind :goto) (:outer? ctx)))
+      (errors/fail :marker-position "Labels require the outer body; goto requires a statement outside operands and initializers"
+                   {:operator kind :label name} (:site ctx)))
+    (expression ctx {:hint nil :var-meta {}}
+                (if (= kind :label) {:op :label :name name}
+                    {:op :goto :name name}))))
+
+(np/defpasses
+  (parse-operand
+    {:from [l/Bound Raw] :to (t/HVec [l/Bound-Supply l/Bound-Expr])
+     :args [l/Bound-Supply l/Bound-Context] :body true}
+    [supply ctx form]
+    (parse supply (assoc ctx :statement? false :tail? false :outer? false :transfer? false) form))
+
+  (parse-forms
+    {:from [l/Bound Forms] :to (t/HVec [l/Bound-Supply (t/Vec l/Bound-Expr)])
+     :args [l/Bound-Supply l/Bound-Context] :body true}
+    [supply ctx forms]
+    (reduce (fn [^{::t/- (t/HVec [l/Bound-Supply (t/Vec l/Bound-Expr)])} acc ^{::t/- l/Bound-Raw} form]
+              (let [[supply out] acc
+                    last? (= (count out) (dec (count forms)))
+                    ctx (assoc ctx :tail? (and (:tail? ctx) last?))
+                    [supply expr] (if (:statement? ctx) (parse supply ctx form) (parse-operand supply ctx form))]
+                [supply (conj out expr)]))
+            [supply []] forms))
+
+  (parse-body
+    {:from [l/Bound Forms] :to (t/HVec [l/Bound-Supply l/Bound-Expr])
+     :args [l/Bound-Supply l/Bound-Context] :body true}
+    [supply ctx forms]
+    (let [[supply forms] (parse-forms supply (assoc ctx :statement? true) forms)]
+      [supply (expression ctx {:hint nil :var-meta {}} {:op :do :forms forms})]))
+
+  (parse-bindings
+    {:from [l/Bound Raw] :to (t/HVec [l/Bound-Supply l/Bound-Context (t/Vec l/Bound-Binding)])
+     :args [l/Bound-Supply l/Bound-Context (t/U ':mutable ':immutable ':loop)] :body true}
+    [supply ctx mode bindings]
+    (if (and (vector? bindings) (even? (count bindings)))
+      (reduce (fn [^{::t/- (t/HVec [l/Bound-Supply l/Bound-Context (t/Vec l/Bound-Binding)])} acc
+                   ^{::t/- (t/Seq t/Any)} pair]
+                (let [[supply ctx out] acc
+                      [supply init] (parse-operand supply ctx (second pair))
+                      [supply ctx local] (bind-local supply ctx (not= mode :immutable) (:jvm init) (first pair))
+                      local (cond-> local (= mode :loop) (assoc :mutable? false))
+                      ctx (assoc ctx :locals (assoc (:locals ctx) (:name local) local))
+                      hint (jvm/tag-class (:hint (:jvm local)))
+                      init (if (and hint (= mode :immutable))
+                             (expression ctx {:hint nil :var-meta {}} {:op :host :operation {:op :adapt :class hint} :args [init]}) init)]
+                  [supply ctx (conj out {:local local :init init})])) [supply ctx []] (partition 2 bindings))
+      (errors/fail :invalid-bindings "Expected an even binding vector" {} (:site ctx))))
+
+  (parse-scope
+    {:from [l/Bound Forms] :to (t/HVec [l/Bound-Supply l/Bound-Expr])
+     :args [l/Bound-Supply l/Bound-Context t/Bool] :body true}
+    [supply ctx mutable? forms]
+    (let [inner (assoc ctx :outer? mutable?)
+          [supply inner bindings] (parse-bindings supply inner (if mutable? :mutable :immutable) (second forms))
+          [supply body] (parse-body supply inner (vec (drop 2 forms)))]
+      [supply (expression ctx {:hint (jvm/source-tag (:form (:site ctx))) :var-meta {}}
+                          {:op :let :bindings bindings :body body})]))
+
+  (parse-loop
+    {:from [l/Bound Forms] :to (t/HVec [l/Bound-Supply l/Bound-Expr])
+     :args [l/Bound-Supply l/Bound-Context] :body true}
+    [supply ctx forms]
+    (let [[supply id] (fresh "loop" supply)
+          inner (assoc ctx :outer? false :recur nil :tail? false)
+          [supply inner bindings] (parse-bindings supply inner :loop (second forms))
+          target {:id id :bindings (mapv :local bindings)}
+          [supply body] (parse-body supply (assoc inner :recur target :tail? true) (vec (drop 2 forms)))]
+      [supply (first (loops/solve {} (expression ctx {:hint nil :var-meta {}} {:op :loop :id id :bindings bindings :body body})))]))
+
+  (parse-recur
+    {:from [l/Bound Forms] :to (t/HVec [l/Bound-Supply l/Bound-Expr])
+     :args [l/Bound-Supply l/Bound-Context] :body true}
+    [supply ctx forms]
+    (let [target (:recur ctx)]
+      (when-not (and target (:tail? ctx))
+        (errors/fail :invalid-recur "recur requires the tail position of a local loop" {} (:site ctx)))
+      (when-not (= (count (:bindings target)) (dec (count forms)))
+        (errors/fail :recur-arity "recur argument count does not match its target" {} (:site ctx)))
+      (let [[supply args] (parse-forms supply (assoc ctx :statement? false :tail? false) (subvec forms 1))]
+        [supply (expression ctx {:hint nil :var-meta {}} {:op :recur :target target :args args})])))
+
+  (parse-arms
+    {:from [l/Bound Forms] :to (t/HVec [l/Bound-Supply l/Bound-Exprs])
+     :args [l/Bound-Supply l/Bound-Context] :body true}
+    [supply ctx forms]
+    (reduce (fn [^{::t/- (t/HVec [l/Bound-Supply l/Bound-Exprs])} acc ^{::t/- t/Any} form]
+              (let [[supply arms] acc [supply arm] (parse supply (assoc ctx :outer? false) form)] [supply (conj arms arm)]))
+            [supply []] forms))
+
+  (parse-case
+    {:from [l/Bound Forms] :to (t/HVec [l/Bound-Supply l/Bound-Expr])
+     :args [l/Bound-Supply l/Bound-Context] :body true}
+    [supply ctx forms]
+    (let [{:keys [selector arms]} (cases/expand-case (:macro-env ctx) forms)]
+      (if (empty? arms) (parse supply ctx selector)
+        (do
+          (when-not (and (seq? selector) (= 3 (count selector)))
+            (errors/fail :invalid-case "Expected core case's selector binding" {} (:site ctx)))
+          (let [[supply inner bindings] (parse-bindings supply ctx :immutable (second selector))
+                [supply selector] (parse-operand supply inner (first (nnext selector)))
+                [supply arms] (parse-arms supply inner arms)
+                [supply body] (case-expression supply ctx selector arms)]
+            [supply (expression ctx {:hint nil :var-meta {}} {:op :let :bindings bindings :body body})])))))
+
+  (parse-dispatch
+    {:from [l/Bound Forms] :to (t/HVec [l/Bound-Supply l/Bound-Expr])
+     :args [l/Bound-Supply l/Bound-Context] :body true}
+    [supply ctx forms]
+    (let [{:keys [value spec arms]} (cases/dispatch-syntax forms)
+          [supply value] (parse-operand supply ctx value)]
+      (when-not (= :local (:op (:node value)))
+        (errors/fail :invalid-case "case* dispatch must reference a lexical local" {} (:site ctx)))
+      (let [selector (expression ctx {:hint nil :var-meta {}} {:op :dispatch :value value :spec spec})
+            [supply arms] (parse-arms supply ctx arms)]
+        (case-expression supply ctx selector arms))))
+
+  (parse-call
+    {:from [l/Bound Forms] :to (t/HVec [l/Bound-Supply l/Bound-Expr])
+     :args [l/Bound-Supply l/Bound-Context] :body true}
+    [supply ctx forms]
+    (let [inner (assoc ctx :statement? false :tail? false)
+          [supply function] (parse-operand supply inner (first forms))
+          arity (dec (count forms))
+          direct (facts/direct-call arity function)
+          metadata (:var-meta (:jvm function))
+          primitive (when-not direct (facts/primitive-interface arity (:arglists metadata)))
+          hint (or (jvm/source-tag (:form (:site ctx)))
+                   (jvm/source-tag (facts/signature arity (:arglists metadata)))
+                   (when (and (not direct) (not primitive) (= :global (:op (:node function)))) (:hint (:jvm function)))
+                   (:tag metadata))
+          [supply args] (parse-forms supply inner (vec (rest forms)))
+          class-arg (some-> args first :node)
+          node (cond
+                 (and (= :global (:op (:node function))) (= 'clojure.core/instance? (:name (:node function)))
+                      (= 2 (count args)) (= :literal (:op class-arg)) (class? (:value class-arg)))
+                 {:op :host :operation {:op :instanceof :class (:value class-arg)} :args [(nth args 1)]}
+                 direct {:op :host :operation direct :args args}
+                 primitive {:op :host :operation {:op :instance :class primitive :member 'invokePrim :mode :method :tags nil}
+                            :args (into [function] args)}
+                 :else {:op :invoke :function function :args args})]
+      (let [method (when-not (or direct primitive) (facts/protocol-call arity function))
+            fnode (:node function) key (:form (:site function))]
+        (cond
+          (and method (= :global (:op fnode)))
+          (protocol-call supply ctx {:hint hint :var-meta {}} method (:name fnode) args)
+          (and (= :literal (:op fnode)) (keyword? key) (= 1 arity)
+               (effects (nth args 0)))
+          (keyword-call supply ctx {:hint hint :var-meta {}} key (nth args 0))
+          :else [supply (expression ctx {:hint hint :var-meta {}} node)]))))
+
+  (parse-host
+    {:from [l/JVM Syntax] :to (t/HVec [l/Bound-Supply l/Bound-Expr])
+     :args [l/Bound-Supply l/Bound-Context] :body true}
+    [supply ctx syntax]
+    (let [[supply args] (parse-forms supply (assoc ctx :statement? false :tail? false) (:operands syntax))]
+      [supply (expression ctx {:hint (jvm/source-tag (:form (:site ctx))) :var-meta {}}
+                          {:op :host :operation (:operation syntax) :args args})]))
+
+  (parse-assignment
+    {:from [l/Bound Forms] :to (t/HVec [l/Bound-Supply l/Bound-Expr])
+     :args [l/Bound-Supply l/Bound-Context (t/U ':setq ':psetq)] :body true}
+    [supply ctx kind forms]
+    (if (even? (count forms))
+      (let [[supply pairs]
+            (reduce (fn [^{::t/- (t/HVec [l/Bound-Supply l/Bound-Assignments])} acc ^{::t/- (t/Seq t/Any)} pair]
+                      (let [[supply pairs] acc target (first pair)]
+                        (when-not (simple-symbol? target)
+                          (errors/fail :invalid-assignment "Assignment targets must be simple symbols" {:binding target} (:site ctx)))
+                        (let [local (or (get (:locals ctx) target)
+                                        (errors/fail :unknown-assignment "Expected a mutable loophole local" {:binding target} (:site ctx)))]
+                          (when-not (:mutable? local)
+                            (errors/fail :immutable-assignment "Cannot assign to an immutable local" {:binding target} (:site ctx)))
+                          (let [[supply value] (parse-operand supply ctx (second pair))]
+                            [supply (conj pairs {:target local :value value})])))) [supply []] (partition 2 forms))]
+        [supply (expression ctx {:hint (jvm/source-tag (:form (:site ctx))) :var-meta {}}
+                            (if (= kind :setq) {:op :setq :pairs pairs} {:op :psetq :pairs pairs}))])
+      (errors/fail :invalid-assignment "setq/psetq requires symbol/value pairs" {} (:site ctx))))
+
+  (parse-list
+    {:from [l/Bound Forms] :to (t/HVec [l/Bound-Supply l/Bound-Expr])
+     :args [l/Bound-Supply l/Bound-Context] :body true}
+    [supply ctx forms]
+    (let [op (first forms) form (:form (:site ctx)) resolved (resolve-name ctx op)
+          marker (when (var? resolved) (get (:markers ctx) resolved))
+          _ (when (var? resolved) (errors/check-visibility resolved (:site ctx)))]
+      (cond
+        (= :loophole marker)
+        (errors/fail :unsupported-form "Nested loophole regions are unsupported" {:operator op} (:site ctx))
+        (= :setq marker) (parse-assignment supply ctx :setq (vec (rest forms)))
+        (= :psetq marker) (parse-assignment supply ctx :psetq (vec (rest forms)))
+        (= :label marker) [supply (parse-marker ctx :label forms)]
+        (= :goto marker) [supply (parse-marker ctx :goto forms)]
+        (= op 'quote)
+        (if (= 2 (count forms))
+          [supply (expression ctx {:hint nil :var-meta {}} {:op :literal :value (second forms)})]
+          (errors/fail :invalid-quote "quote requires one argument" {} (:site ctx)))
+        (= op 'var)
+        (let [name (second forms) resolved (when (symbol? name) (ns-resolve *ns* name))]
+          (if (and (= 2 (count forms)) (var? resolved))
+            [supply (expression ctx {:hint nil :var-meta {}}
+                                {:op :var :name (.toSymbol ^clojure.lang.Var resolved)})]
+            (errors/fail :invalid-var "var requires a resolvable Var name" {} (:site ctx))))
+        (#{'fn* 'letfn* 'try 'set! 'monitor-enter 'monitor-exit} op)
+        (errors/fail :unsupported-form "Form is unsupported inside loophole; place it outside the region" {:operator op} (:site ctx))
+        (#{'new '.} op) (parse-host supply ctx (or (host/syntax ctx forms)
+                                                  (errors/fail :invalid-interop "Missing host syntax" {} (:site ctx))))
+        (= op 'do) (parse-body supply ctx (vec (rest forms)))
+        (= op 'let*) (parse-scope supply ctx false forms)
+        (= op 'loop*) (parse-loop supply ctx forms)
+        (= op 'recur) (parse-recur supply ctx forms)
+        (= op 'case*) (parse-dispatch supply ctx forms)
+        (and (= resolved #'clojure.core/case) (< (count forms) 2))
+        (errors/fail :invalid-case "case requires a dispatch expression" {} (:site ctx))
+        (= resolved #'clojure.core/case) (parse-case supply ctx forms)
+        (= op 'if)
+        (if (<= 3 (count forms) 4)
+          (let [[supply test] (parse-operand supply ctx (second forms))
+                [supply yes] (parse supply (assoc ctx :outer? false) (nth forms 2))
+                [supply no] (parse supply (assoc ctx :outer? false) (get forms 3))]
+            [supply (expression ctx {:hint (jvm/source-tag form) :var-meta {}} {:op :if :test test :then yes :else no})])
+          (errors/fail :invalid-if "if requires a test, then, and optional else" {} (:site ctx)))
+        (= op 'throw)
+        (if (= 2 (count forms))
+          (let [[supply value] (parse-operand supply ctx (second forms))]
+            [supply (expression ctx {:hint nil :var-meta {}} {:op :throw :value value})])
+          (errors/fail :invalid-throw "throw requires one value" {} (:site ctx)))
+        (and (symbol? op) (special-symbol? op))
+        (errors/fail :unsupported-form "Special form is not supported inside loophole" {:operator op} (:site ctx))
+        (and (var? resolved) (:macro (meta resolved)))
+        (let [expanded (.applyTo ^clojure.lang.Var resolved (cons form (cons (:macro-env ctx) (rest forms))))
+              expanded (if (instance? clojure.lang.IObj expanded)
+                         (with-meta expanded (merge (meta expanded) (meta form))) expanded)]
+          (parse supply ctx expanded))
+        (and (var? resolved) (ifn? (:inline (meta resolved)))
+             (let [arity (:inline-arities (meta resolved))] (or (nil? arity) (and (ifn? arity) (.invoke ^clojure.lang.IFn arity (dec (count forms)))))))
+        (let [inline (:inline (meta resolved))
+              expanded (if (ifn? inline) (.applyTo ^clojure.lang.IFn inline (rest forms))
+                           (errors/fail :invalid-expression "Invalid inline expander" {} (:site ctx)))
+              expanded (if (instance? clojure.lang.IObj expanded)
+                         (with-meta expanded (merge (meta expanded) (meta form))) expanded)]
+          (parse supply ctx expanded))
+        :else (if-let [syntax (host/syntax ctx forms)]
+                (parse-host supply ctx syntax)
+                (parse-call supply ctx forms)))))
+
+  (parse-collection
+    {:from [l/Bound Raw] :to (t/HVec [l/Bound-Supply l/Bound-Expr])
+     :args [l/Bound-Supply l/Bound-Context] :body true}
+    [supply ctx form]
+    (if (or (vector? form) (map? form) (set? form))
+      (let [kind (cond (vector? form) :vector (map? form) :map :else :set)
+            ;; Freeze source traversal before alpha-renaming changes element hashes.
+            items (if (map? form) (vec (mapcat identity form)) (vec form))
+            inner (assoc ctx :statement? false :tail? false)
+            [supply items] (parse-forms supply inner items)
+            [supply metadata] (if (some? (meta form)) (parse-operand supply inner (meta form)) [supply nil])]
+        [supply (expression ctx {:hint (jvm/source-tag form) :var-meta {}}
+                            {:op :collection :kind kind :items items :metadata metadata})])
+      (errors/fail :invalid-expression "Expected a collection" {} (:site ctx))))
+
+  (parse
+    {:from [l/Bound Raw] :to (t/HVec [l/Bound-Supply l/Bound-Expr])
+     :args [l/Bound-Supply l/Bound-Context] :body true}
+    [supply ctx form]
+    (let [ctx (assoc ctx :site (errors/site (:site ctx) form))]
+      (cond
+        (symbol? form) [supply (parse-symbol ctx form)]
+        (and (seq? form) (seq form)) (parse-list supply ctx (vec form))
+        (or (vector? form) (map? form) (set? form))
+        (parse-collection supply ctx form)
+        :else [supply (expression ctx {:hint (jvm/source-tag form) :var-meta {}} {:op :literal :value form})]))))
diff --git a/src/com/tailrecursion/blacktide/compiler/source_case.clj b/src/com/tailrecursion/blacktide/compiler/passes/source_case.clj
similarity index 86%
rename from src/com/tailrecursion/blacktide/compiler/source_case.clj
rename to src/com/tailrecursion/blacktide/compiler/passes/source_case.clj
index 7d565c7..2427814 100644
--- a/src/com/tailrecursion/blacktide/compiler/source_case.clj
+++ b/src/com/tailrecursion/blacktide/compiler/passes/source_case.clj
@@ -1,12 +1,12 @@
-(ns ^:typed.clojure com.tailrecursion.blacktide.compiler.source-case
+(ns ^:typed.clojure com.tailrecursion.blacktide.compiler.passes.source-case
   "Ask core case to select source-arm ordinals. Constants stay opaque, while
   source bodies remain unique. Decode prepared case* tables without evaluating them."
   (:require [com.tailrecursion.nanopass :as np]
-            [com.tailrecursion.blacktide.compiler.source-languages :as s]
+            [com.tailrecursion.blacktide.compiler.languages :as l]
             [typed.clojure :as-alias t]))
 
 (np/defpass expand-case
-  {:from [s/Bound Forms] :to [s/Bound CaseSyntax] :args [(t/Map t/Sym t/Any)] :body true}
+  {:from [l/Bound Forms] :to [l/Bound CaseSyntax] :args [(t/Map t/Sym t/Any)] :body true}
   [env forms]
   (let [_ (when (< (count forms) 2)
             (.applyTo ^clojure.lang.Var #'clojure.core/case (cons (apply list forms) (cons env (rest forms)))))
@@ -17,12 +17,12 @@
         form (list* 'clojure.core/case (second forms) clauses)]
     {:selector (.applyTo ^clojure.lang.Var #'clojure.core/case (cons form (cons env (rest form)))) :arms arms}))
 
-(np/defpass int32 {:from [s/Bound Raw] :to t/Int :body true}
+(np/defpass int32 {:from [l/Bound Raw] :to t/Int :body true}
   [value]
   (if (instance? Number value) (.intValue ^Number value)
       (throw (IllegalArgumentException. "case* expects numeric dispatch data"))))
 
-(np/defpass dispatch-syntax {:from [s/Bound Forms] :to [s/Bound DispatchSyntax] :body true}
+(np/defpass dispatch-syntax {:from [l/Bound Forms] :to [l/Bound DispatchSyntax] :body true}
   [forms]
   (let [_ (when-not (<= 8 (count forms) 9) (throw (IllegalArgumentException. "Malformed case*")))
         [_ value shift mask default table switch test skip] forms]
@@ -30,7 +30,7 @@
                    (#{:int :hash-equiv :hash-identity} test) (or (nil? skip) (set? skip)))
       (throw (IllegalArgumentException. "Malformed case* dispatch table")))
     (let [[tests arms]
-          (reduce (fn [^{::t/- (t/HVec [(t/Map t/Int (t/HVec [t/Any t/Int])) s/Bound-Forms])} acc
+          (reduce (fn [^{::t/- (t/HVec [(t/Map t/Int (t/HVec [t/Any t/Int])) l/Bound-Forms])} acc
                        ^{::t/- (t/HVec [t/Any t/Any])} entry]
                     (let [[tests arms] acc [key pair] entry]
                       (when-not (and (vector? pair) (= 2 (count pair)))
diff --git a/src/com/tailrecursion/blacktide/compiler/source_host.clj b/src/com/tailrecursion/blacktide/compiler/passes/source_host.clj
similarity index 93%
rename from src/com/tailrecursion/blacktide/compiler/source_host.clj
rename to src/com/tailrecursion/blacktide/compiler/passes/source_host.clj
index 5e8f5b7..431e186 100644
--- a/src/com/tailrecursion/blacktide/compiler/source_host.clj
+++ b/src/com/tailrecursion/blacktide/compiler/passes/source_host.clj
@@ -1,9 +1,8 @@
-(ns ^:typed.clojure com.tailrecursion.blacktide.compiler.source-host
+(ns ^:typed.clojure com.tailrecursion.blacktide.compiler.passes.source-host
   "Recognize native JVM syntax with the source lexical environment. Return host
   descriptors and ordered source operands; none of those operands is evaluated."
   (:require [com.tailrecursion.nanopass :as np]
-            [com.tailrecursion.blacktide.compiler.source-languages :as s]
-            [com.tailrecursion.blacktide.compiler.jvm-languages :as l]
+            [com.tailrecursion.blacktide.compiler.languages :as l]
             [com.tailrecursion.blacktide.compiler.jvm :as jvm]
             [typed.clojure :as-alias t]))
 
@@ -28,7 +27,7 @@
               :else {:op :static :class class :member (clojure.core/symbol member) :tags tags})))))
 
 (np/defpass syntax
-  {:from [s/Bound Forms] :to (t/Option l/JVM-Syntax) :args [s/Bound-Context] :body true}
+  {:from [l/Bound Forms] :to (t/Option l/JVM-Syntax) :args [l/Bound-Context] :body true}
   [ctx forms]
   (let [op (first forms) args (subvec forms 1)]
     (cond
diff --git a/src/com/tailrecursion/blacktide/compiler/source_jvm.clj b/src/com/tailrecursion/blacktide/compiler/passes/source_jvm.clj
similarity index 95%
rename from src/com/tailrecursion/blacktide/compiler/source_jvm.clj
rename to src/com/tailrecursion/blacktide/compiler/passes/source_jvm.clj
index 45d2fc2..9f9adf0 100644
--- a/src/com/tailrecursion/blacktide/compiler/source_jvm.clj
+++ b/src/com/tailrecursion/blacktide/compiler/passes/source_jvm.clj
@@ -1,9 +1,8 @@
-(ns ^:typed.clojure com.tailrecursion.blacktide.compiler.source-jvm
+(ns ^:typed.clojure com.tailrecursion.blacktide.compiler.passes.source-jvm
   "Infer source expression and binding facts before normalization erases native
   typing evidence. Mutable locals use declared hints, never initializer guesses."
   (:require [com.tailrecursion.nanopass :as np]
-            [com.tailrecursion.blacktide.compiler.source-languages :as s]
-            [com.tailrecursion.blacktide.compiler.jvm-languages :as l]
+            [com.tailrecursion.blacktide.compiler.languages :as l]
             [com.tailrecursion.blacktide.compiler.jvm :as jvm]
             [typed.clojure :as-alias t]))
 
@@ -28,7 +27,7 @@
         (clojure.lang.RT/classForNameNonLoading name)))))
 
 (np/defpass direct-call
-  {:from [s/Bound Expr] :to (t/Option l/JVM-Operation) :args [t/Int] :body true}
+  {:from [l/Bound Expr] :to (t/Option l/JVM-Operation) :args [t/Int] :body true}
   [arity function]
   (let [node (:node function)]
     (when (and (:direct-linking *compiler-options*) (= :global (:op node)))
@@ -55,7 +54,7 @@
   (when (or (class? on) (symbol? on)) (clojure.lang.Compiler$HostExpr/maybeClass on false)))
 
 (np/defpass protocol-call
-  {:from [s/Bound Expr] :to (t/Option l/JVM-Protocol) :args [t/Int] :body true}
+  {:from [l/Bound Expr] :to (t/Option l/JVM-Protocol) :args [t/Int] :body true}
   [arity function]
   ;; InvokeExpr reads protocol metadata after analyzing the operands. A macro
   ;; in an argument may change it; later source must not change this call.
@@ -76,7 +75,7 @@
             {:class nil :member nil :parameters []}))))))
 
 (np/defpass infer
-  {:from [s/Bound Node] :to [l/JVM Fact] :args [l/JVM-Seed] :body true}
+  {:from [l/Bound Node] :to [l/JVM Fact] :args [l/JVM-Seed] :body true}
   [seed node]
   (let [hint (when (#{:local :global :invoke :host} (:op node)) (jvm/tag-class (:hint seed)))
         fact (fn ^{::t/- [l/JVM-Type t/Bool :-> l/JVM-Fact]} describe [type primitive?]
@@ -112,7 +111,7 @@
             (assoc (fact (jvm/join-type (:type a) (:type b))
                          (and (:primitive? a) (:primitive? b) (= (:type a) (:type b))))
                    :hint-error (or (:hint-error a) (:hint-error b))))
-      :case (let [values (reduce (fn [^{::t/- (t/Vec l/JVM-Fact)} values ^{::t/- s/Bound-Expr} arm]
+      :case (let [values (reduce (fn [^{::t/- (t/Vec l/JVM-Fact)} values ^{::t/- l/Bound-Expr} arm]
                                   (if (= :throw (:op (:node arm))) values (conj values (:jvm arm)))) [] (:arms node))
                   type (some-> values first :type)
                   type (if (and (class? type) (every? #(and (= type (:type %)) (nil? (:hint-error %))) values)) type :unknown)]
diff --git a/src/com/tailrecursion/blacktide/compiler/passes/source_values.clj b/src/com/tailrecursion/blacktide/compiler/passes/source_values.clj
new file mode 100644
index 0000000..2705187
--- /dev/null
+++ b/src/com/tailrecursion/blacktide/compiler/passes/source_values.clj
@@ -0,0 +1,38 @@
+(ns ^:typed.clojure com.tailrecursion.blacktide.compiler.passes.source-values
+  "Restore primitive source-local reads before compiler temporaries exist.
+  Clojure boxes each Object use of a primitive local independently; a machine
+  slot must not turn those reads into aliases of one box. Native trees already
+  preserve this behavior, and mutable locals have reference semantics."
+  (:require [com.tailrecursion.nanopass :as np]
+            [com.tailrecursion.blacktide.compiler.languages :as l]
+            [typed.clojure :as-alias t]))
+
+(np/defpasses
+  (node
+    {:from [l/Classified Node] :to [l/Classified Node]
+     :processors {Expr expression Exprs forms Binding binding-form Assignments pairs
+                  Local clojure.core/identity Raw clojure.core/identity
+                  CollectionKind clojure.core/identity RecurTarget clojure.core/identity CaseSpec clojure.core/identity}}
+    [node]
+    :native node)
+
+  (binding-form {:from [l/Classified Binding] :to [l/Classified Binding]
+                            :processors {Expr expression Local clojure.core/identity}} [binding])
+
+  (pair {:from [l/Classified Assignment] :to [l/Classified Assignment]
+                    :processors {Expr expression Local clojure.core/identity}} [assignment])
+
+  (pairs {:from [l/Classified Assignments] :to [l/Classified Assignments]
+                     :processors {Assignment pair}} [assignments])
+
+  (forms {:from [l/Classified Exprs] :to [l/Classified Exprs] :processors {Expr expression}} [exprs])
+
+  (expression {:from [l/Classified Expr] :to [l/Classified Expr] :body true}
+    [expr]
+    (let [expr (assoc expr :node (node (:node expr)))
+          type (:type (:jvm expr))]
+      (if (and (= :local (:op (:node expr))) (:primitive? (:jvm expr))
+               (class? type) (.isPrimitive ^Class type) (not= Void/TYPE type))
+        (assoc expr :node {:op :host :operation {:op :static :class clojure.lang.RT
+                                               :member (symbol (str (.getName ^Class type) "Cast")) :tags [type]}
+                           :args [expr]}) expr))))
diff --git a/src/com/tailrecursion/blacktide/compiler/storage.clj b/src/com/tailrecursion/blacktide/compiler/passes/storage.clj
similarity index 81%
rename from src/com/tailrecursion/blacktide/compiler/storage.clj
rename to src/com/tailrecursion/blacktide/compiler/passes/storage.clj
index 4bb8ea6..887ad3e 100644
--- a/src/com/tailrecursion/blacktide/compiler/storage.clj
+++ b/src/com/tailrecursion/blacktide/compiler/passes/storage.clj
@@ -1,4 +1,4 @@
-(ns ^:typed.clojure com.tailrecursion.blacktide.compiler.storage
+(ns ^:typed.clojure com.tailrecursion.blacktide.compiler.passes.storage
   "Select primitive storage only when every producer and consumer agrees.
   Object observations and reference-valued native reads retain identity; loop slots
   specialize only to long/double. Observed copies share boxing requirements;
@@ -6,15 +6,13 @@
   (:require [clojure.set :as set]
             [com.tailrecursion.nanopass :as np]
             [com.tailrecursion.blacktide.compiler.languages :as l]
-            [com.tailrecursion.blacktide.compiler.analysis-languages :as a]
-            [com.tailrecursion.blacktide.compiler.structured-languages :as s]
-            [com.tailrecursion.blacktide.compiler.graph :as graph]
-            [com.tailrecursion.blacktide.compiler.native :as native]
-            [com.tailrecursion.blacktide.compiler.value-facts :as facts]
-            [com.tailrecursion.blacktide.compiler.box-origins :as origins]
+            [com.tailrecursion.blacktide.compiler.passes.graph :as graph]
+            [com.tailrecursion.blacktide.compiler.passes.native :as native]
+            [com.tailrecursion.blacktide.compiler.passes.value-facts :as facts]
+            [com.tailrecursion.blacktide.compiler.passes.box-origins :as origins]
             [typed.clojure :as-alias t]))
 
-(np/defpass loop-slots {:from [s/Joined Expr] :to (t/Set t/Sym)}
+(np/defpass loop-slots {:from [l/Joined Expr] :to (t/Set t/Sym)}
   [node]
   :bind (loop-slots (:body node))
   :join (set/union (loop-slots (:value node)) (loop-slots (:body node)))
@@ -23,7 +21,7 @@
   :loop (into (loop-slots (:body node)) (:slots node))
   :recur #{} :return #{} :throw #{})
 
-(np/defpass cyclic? {:from [s/Joined Expr] :to t/Bool}
+(np/defpass cyclic? {:from [l/Joined Expr] :to t/Bool}
   [node]
   :bind (cyclic? (:body node))
   :join (or (cyclic? (:value node)) (cyclic? (:body node)))
@@ -32,13 +30,13 @@
   :loop true :recur true :return false :throw false)
 
 (np/defpass operand-boxes
-  {:from [l/Machine Operand] :to (t/Set t/Sym) :args [s/Stored-Types] :body true}
+  {:from [l/Machine Operand] :to (t/Set t/Sym) :args [l/Stored-Types] :body true}
   [types operand]
   (let [type (facts/atom-type types (:value operand))]
     (if (and (class? type) (= type (:type (:jvm operand)))) #{} (graph/atom-reads (:value operand)))))
 
 (np/defpass operation-boxes
-  {:from [l/Machine Operation] :to (t/Set t/Sym) :args [s/Stored-Types (t/Set t/Sym)] :body true}
+  {:from [l/Machine Operation] :to (t/Set t/Sym) :args [l/Stored-Types (t/Set t/Sym)] :body true}
   [types observed operation]
   (let [rhs (:value operation)]
     (case (:op rhs)
@@ -68,7 +66,7 @@
          (if (= observed next) observed (recur next)))) initial)))
 
 (np/defpass term-boxes
-  {:from [l/Machine Term] :to (t/Set t/Sym) :args [s/Stored-Types (t/Set t/Sym)] :body true}
+  {:from [l/Machine Term] :to (t/Set t/Sym) :args [l/Stored-Types (t/Set t/Sym)] :body true}
   [types reboxable term]
   (case (:op term)
     :return (if (and (= :local (:op (:value term))) (contains? reboxable (:id (:value term))))
@@ -78,7 +76,7 @@
     (graph/term-reads term)))
 
 (np/defpass select-storage
-  {:from [s/Joined Expr] :to [s/Stored Program] :args [a/Analysis-Program] :body true}
+  {:from [l/Joined Expr] :to [l/Stored Program] :args [l/Analysis-Program] :body true}
   [analysis expr]
   ;; Clojure wraps an expression-position loop in an Object-returning call.
   ;; Acyclic code has no such boundary: keep its return boxed so outer numeric
@@ -86,13 +84,13 @@
   (let [reboxable (if (cyclic? expr) (origins/reboxable-values analysis) #{})
         slots (loop-slots expr)
         initial (reduce-kv
-                  (fn [^{::t/- s/Stored-Types} out ^{::t/- t/Sym} id ^{::t/- a/Analysis-Type} type]
+                  (fn [^{::t/- l/Stored-Types} out ^{::t/- t/Sym} id ^{::t/- l/Analysis-Type} type]
                     (if (and (class? type) (or (not (contains? slots id)) (#{Long/TYPE Double/TYPE} type)))
                       (assoc out id type) out)) {} (:types analysis))
         machine (update (:machine analysis) :blocks #(select-keys % (:reachable analysis)))
         observed (observed-values machine)
         blocks (vals (:blocks machine))
-        types ((fn ^{::t/- [s/Stored-Types :-> s/Stored-Types]} solve [types]
+        types ((fn ^{::t/- [l/Stored-Types :-> l/Stored-Types]} solve [types]
                  (let [boxed (into (into #{} (mapcat #(operation-boxes types observed %)) (mapcat :ops blocks))
                                    (mapcat #(term-boxes types reboxable (:term %))) blocks)
                        next (apply dissoc types boxed)]
diff --git a/src/com/tailrecursion/blacktide/compiler/passes/structure.clj b/src/com/tailrecursion/blacktide/compiler/passes/structure.clj
new file mode 100644
index 0000000..46216ef
--- /dev/null
+++ b/src/com/tailrecursion/blacktide/compiler/passes/structure.clj
@@ -0,0 +1,160 @@
+(ns ^:typed.clojure com.tailrecursion.blacktide.compiler.passes.structure
+  "Recover acyclic control or a single-header loop with bounded duplication.
+  Construction records block identities to preserve native construction and
+  cache sites. Dead native code keeps the dispatcher for source validation."
+  (:require [com.tailrecursion.nanopass :as np]
+            [com.tailrecursion.blacktide.compiler.languages :as l]
+            [com.tailrecursion.blacktide.compiler.passes.graph :as graph]
+            [typed.clojure :as-alias t]))
+
+(np/defpass block
+  {:from [l/Machine Label] :to [l/Machine Block] :args [l/Machine-Program] :body true}
+  [machine id]
+  (or (get (:blocks machine) id) (throw (IllegalStateException. "Missing checked block"))))
+
+(np/defpass scan
+  {:from [l/Machine Label] :to [l/Joined Scan]
+   :args [l/Machine-Program (t/Set l/Machine-Label) l/Joined-Scan] :body true}
+  [machine path state id]
+  (let [[seen headers] state]
+    (cond (contains? path id) [seen (conj headers id)]
+          (contains? seen id) state
+          :else (reduce (fn [^{::t/- l/Joined-Scan} state ^{::t/- l/Machine-Label} next]
+                          (scan machine (conj path id) state next))
+                        [(conj seen id) headers]
+                        (graph/successors (:term (block machine id)))))))
+
+(np/defpass harmless-atom? {:from [l/Machine Atom] :to t/Bool}
+  [atom]
+  :local true
+  :literal (nil? (:value atom)))
+
+(np/defpass harmless-block? {:from [l/Machine Block] :to t/Bool :body true}
+  [block]
+  ;; Lowering leaves nil result copies after transfers. No host/native form or
+  ;; arbitrary quoted constant may disappear before Clojure validates it.
+  (and (every? (fn [op] (let [rhs (:value op)]
+                         (and (= :value (:op rhs)) (harmless-atom? (:value rhs))))) (:ops block))
+       (let [term (:term block)]
+         (case (:op term)
+           :jump true
+           (:return :throw) (harmless-atom? (:value term))
+           (:branch :switch) (harmless-atom? (:test term))))))
+
+(np/defpass wrap
+  {:from [l/Machine Block] :to [l/Joined Expr] :args [l/Joined-Expr] :body true}
+  [end block]
+  (reduce (fn [^{::t/- l/Joined-Expr} body ^{::t/- l/Machine-Operation} op]
+            {:op :bind :binding op :body body}) end (reverse (:ops block))))
+
+(np/defpasses
+  (arms
+    {:from [l/Machine Term] :to [l/Joined Arms] :args [l/Joined-Context t/Bool t/Int] :body true}
+    [ctx inside? budget term]
+    (if (= :switch (:op term))
+      ((fn ^{::t/- [t/Int t/Int (t/Vec l/Joined-Expr) (t/Vec l/Machine-Label) :-> l/Joined-Arms]} build [index budget out blocks]
+         (if (= index (count (:targets term))) {:op :built :budget budget :arms out :blocks blocks}
+           (let [result (edge ctx inside? budget (nth (:targets term) index))]
+             (if (= :built (:op result))
+               (recur (inc index) (:budget result) (conj out (:expr result)) (into blocks (:blocks result))) result)))) 0 budget [] [])
+      {:op :exhausted}))
+
+  (terminator
+    {:from [l/Machine Term] :to [l/Joined Build] :args [l/Joined-Context t/Bool t/Int]}
+    [ctx inside? budget term]
+    :jump (edge ctx inside? budget (:target term))
+    :return {:op :built :budget budget :blocks [] :expr term}
+    :throw {:op :built :budget budget :blocks [] :expr term}
+    :switch (let [result (arms ctx inside? budget term)]
+              (if (= :built (:op result))
+                {:op :built :budget (:budget result) :blocks (:blocks result) :expr {:op :switch :test (:test term) :arms (:arms result)}} result))
+    :branch (let [yes (edge ctx inside? budget (:yes term))]
+              (if (= :exhausted (:op yes)) yes
+                (let [no (edge ctx inside? (:budget yes) (:no term))]
+                  (if (= :exhausted (:op no)) no
+                    {:op :built :budget (:budget no) :blocks (into (:blocks yes) (:blocks no))
+                     :expr {:op :branch :test (:test term) :yes (:expr yes) :no (:expr no)}})))))
+
+  (joined
+    {:from [l/Planned Join] :to [l/Joined Build] :args [l/Joined-Context t/Bool t/Int l/Machine-Term] :body true}
+    [ctx inside? budget term join]
+    (let [inner (assoc ctx :stop [(:target join) (:result join)])
+          result (terminator inner inside? budget term)]
+      (if (= :built (:op result))
+        (let [tail (edge ctx inside? (:budget result) (:target join))]
+          (if (= :built (:op tail))
+            (assoc tail :blocks (into (:blocks result) (:blocks tail))
+                        :expr {:op :join :target (:result join) :value (:expr result) :body (:expr tail)}) tail))
+        {:op :exhausted})))
+
+  (body
+    {:from [l/Machine Label] :to [l/Joined Build] :args [l/Joined-Context t/Bool t/Int] :body true}
+    [ctx inside? budget id]
+    (let [block (block (:machine ctx) id)
+          budget (- budget (inc (count (:ops block))))]
+      (if (neg? budget) {:op :exhausted}
+        (let [result (if-let [join (get (:joins ctx) id)]
+                       (joined ctx inside? budget (:term block) join)
+                       (terminator ctx inside? budget (:term block)))]
+          (if (= :built (:op result)) (assoc result :blocks (conj (:blocks result) id) :expr (wrap (:expr result) block)) result)))))
+
+  (edge
+    {:from [l/Machine Label] :to [l/Joined Build] :args [l/Joined-Context t/Bool t/Int] :body true}
+    [ctx inside? budget id]
+    (let [stop (:stop ctx)]
+      (cond
+        (and stop (= id (first stop)))
+        {:op :built :budget budget :blocks [] :expr {:op :return :value {:op :local :id (second stop)}}}
+        (= id (:header ctx))
+        (if inside? (if (pos? budget) {:op :built :budget (dec budget) :blocks [] :expr {:op :recur :slots (:slots ctx)}}
+                        {:op :exhausted})
+          (let [result (body ctx true (dec budget) id)]
+            (if (= :built (:op result)) (assoc result :expr {:op :loop :slots (:slots ctx) :body (:expr result)}) result)))
+        :else (body ctx inside? budget id)))))
+
+(np/defpass construction-site?
+  {:from [l/Machine Rhs] :to t/Bool :body true}
+  [rhs]
+  (case (:op rhs)
+    :native true
+    :convert (let [conversion (:conversion rhs)]
+               (and (:parameter? conversion)
+                    (.isAnnotationPresent ^Class (:type conversion) java.lang.FunctionalInterface)))
+    :host (let [operation (:operation rhs)]
+            (case (:op operation)
+              (:keyword-site :method-value) true
+              :adapt (.isAnnotationPresent ^Class (:class operation) java.lang.FunctionalInterface)
+              :protocol (boolean (some (fn [^Class type] (.isAnnotationPresent type java.lang.FunctionalInterface))
+                                       (:parameters (:method operation))))
+              false))
+    false))
+
+(np/defpass site-failure
+  {:from [l/Machine Program] :to (t/Option ':native-sites)
+   :args [(t/Vec l/Machine-Label)] :body true}
+  [blocks machine]
+  (let [copies (frequencies blocks)
+        repeated (mapv (fn [[id _]] (block machine id)) (filter (fn [[_ n]] (> n 1)) copies))]
+    (when (some (fn [block]
+                  (some (fn [operation] (construction-site? (:value operation)))
+                        (:ops block))) repeated)
+      :native-sites)))
+
+(np/defpass recover {:from [l/Planned Program] :to [l/Joined Decision] :body true}
+  [analysis]
+  (let [machine (:machine analysis) reachable (:reachable analysis)
+        [_ headers] (scan machine #{} [#{} #{}] (:entry machine))
+        dead (apply dissoc (:blocks machine) reachable)
+        header (first headers)]
+    (cond
+      (> (count headers) 1) {:op :fallback :reason :cycles}
+      (not (every? harmless-block? (vals dead))) {:op :fallback :reason :validation}
+      :else
+      (let [slots (if header (vec (sort (get (:live machine) header #{}))) [])
+            ctx {:machine machine :header header :slots slots :joins (:joins analysis) :stop nil}
+            budget (* 4 (reduce + 0 (map #(inc (count (:ops %))) (vals (select-keys (:blocks machine) reachable)))))
+            result (edge ctx false budget (:entry machine))]
+        (if (= :built (:op result))
+          (if-let [reason (site-failure (:blocks result) machine)]
+            {:op :fallback :reason reason} {:op :structured :body (:expr result)})
+          {:op :fallback :reason :budget})))))
diff --git a/src/com/tailrecursion/blacktide/compiler/passes/structured_emit.clj b/src/com/tailrecursion/blacktide/compiler/passes/structured_emit.clj
new file mode 100644
index 0000000..135d033
--- /dev/null
+++ b/src/com/tailrecursion/blacktide/compiler/passes/structured_emit.clj
@@ -0,0 +1,108 @@
+(ns ^:typed.clojure com.tailrecursion.blacktide.compiler.passes.structured-emit
+  "Emit lexical control with proven storage classes. Consumer source types stay
+  distinct from storage; Java argument conversions remain explicit evaluations."
+  (:require [com.tailrecursion.nanopass :as np]
+            [com.tailrecursion.blacktide.compiler.languages :as l]
+            [com.tailrecursion.blacktide.compiler.passes.compact :as compact]
+            [com.tailrecursion.blacktide.compiler.passes.collections :as collections]
+            [com.tailrecursion.blacktide.compiler.jvm :as jvm]
+            [typed.clojure :as-alias t]
+            [com.tailrecursion.blacktide.compiler.passes.emit :as emit]
+            [com.tailrecursion.blacktide.compiler.passes.host-emit :as host]
+            [com.tailrecursion.blacktide.compiler.passes.case-emit :as cases]))
+
+(np/defpass primitive-form
+  {:from [l/Machine Code] :to [l/Machine Code] :args [java.lang.Class] :body true}
+  [^Class type form]
+  ;; The parameter conversion is exactly the conversion previously performed by
+  ;; RT.box(type); the matching primitive cast overload itself is an identity.
+  (list (with-meta (symbol "clojure.lang.RT" (str (.getName type) "Cast")) {:param-tags [type]}) form))
+
+(np/defpass binding-form
+  {:from [l/Machine Code] :to [l/Machine Code] :args [l/Stored-Types t/Sym (t/Option java.lang.Class)] :body true}
+  [types id actual form]
+  (if-let [type (get types id)] (if (= type actual) form (primitive-form type form)) (host/box-form form)))
+
+(np/defpasses
+  (atom-form {:from [l/Compact Atom] :to [l/Machine Code] :args [l/Compact-Types]}
+    [types node]
+    :local (:id node)
+    :literal (list 'quote (:value node))
+    :inline (rhs-form types (:value node)))
+
+  (operand-form
+    {:from [l/Compact Operand] :to [l/Machine Code] :args [l/Compact-Types] :body true}
+    [types operand]
+    (let [form (atom-form types (:value operand)) actual (compact/atom-type types (:value operand))
+          fact (:jvm operand)]
+      (if (and (class? actual) (= actual (:type fact))) form
+        (host/typed-form (assoc fact :primitive? false) (if (class? actual) (host/box-form form) form)))))
+
+  (rhs-form
+    {:from [l/Compact Rhs] :to [l/Machine Code] :args [l/Compact-Types]}
+    [types rhs]
+    :host (host/host-form (mapv #(operand-form types %) (:args rhs)) (:operation rhs))
+    :dispatch (cases/selector-form (operand-form types (:value rhs)) (:spec rhs))
+    :convert (let [conversion (:conversion rhs) operand (:value rhs)]
+               (if (and (:parameter? conversion) (.isPrimitive ^Class (:type conversion)))
+                 (if (= (:type conversion) (compact/atom-type types (:value operand)) (:type (:jvm operand)))
+                   (atom-form types (:value operand))
+                   (primitive-form (:type conversion) (operand-form types operand)))
+                 (host/conversion-form conversion (:jvm operand) (atom-form types (:value operand)))))
+    :value (atom-form types (:value rhs))
+    :global (emit/rhs-form rhs) :external (emit/rhs-form rhs) :var (emit/rhs-form rhs) :native (emit/rhs-form rhs)
+    :construct (collections/form (mapv #(atom-form types %) (:items rhs)) (:unique? rhs) (:kind rhs))
+    :metadata (let [value (gensym "value__") metadata (gensym "metadata__")]
+                (list 'let* [(with-meta value {:tag 'clojure.lang.IObj}) (atom-form types (:value rhs))
+                             (with-meta metadata {:tag 'clojure.lang.IPersistentMap}) (atom-form types (:metadata rhs))]
+                      (list '. value 'withMeta metadata)))
+    :invoke (list* (atom-form types (:function rhs)) (mapv #(atom-form types %) (:args rhs)))))
+
+(np/defpass rhs-type
+  {:from [l/Compact Rhs] :to (t/Option java.lang.Class) :args [l/Compact-Types] :body true}
+  [types rhs]
+  (case (:op rhs)
+    :value (compact/atom-type types (:value rhs))
+    :convert (when (:parameter? (:conversion rhs)) (:type (:conversion rhs)))
+    :host (let [type (jvm/host-type (mapv #(:type (:jvm %)) (:args rhs)) (:operation rhs))]
+            (when (and (class? type) (not (#{:adapt} (:op (:operation rhs))))) type))
+    ;; Other producers retain an explicit storage conversion.
+    nil))
+
+(np/defpass operation-form
+  {:from [l/Compact Operation] :to [l/Machine Code] :args [l/Compact-Types] :body true}
+  [types op]
+  (binding-form types (:target op) (rhs-type types (:value op)) (rhs-form types (:value op))))
+
+(np/defpasses
+  (let-form {:from [l/Compact Expr] :to [l/Machine Code] :args [l/Compact-Types] :body true}
+    [types node]
+    ((fn ^{::t/- [(t/Vec t/Any) l/Compact-Expr :-> l/Machine-Code]} bindings [pairs node]
+       (case (:op node)
+         :bind (let [op (:binding node)]
+                 (recur (conj pairs (:target op) (operation-form types op)) (:body node)))
+         :join (recur (conj pairs (:target node)
+                            (binding-form types (:target node) nil (expression types (:value node)))) (:body node))
+         (list 'let* pairs (expression types node)))) [] node))
+
+  (expression {:from [l/Compact Expr] :to [l/Machine Code] :args [l/Compact-Types]}
+    [types node]
+    :bind (let-form types node)
+    :effect (let [binding (:binding node) rhs (:value binding) type (get types (:target binding))
+                  form (rhs-form types rhs)]
+              (list 'do (if (and type (not= type (rhs-type types rhs))) (primitive-form type form) form)
+                    (expression types (:body node))))
+    :join (let-form types node)
+    :branch (list 'if (atom-form types (:test node)) (expression types (:yes node)) (expression types (:no node)))
+    :switch (cases/arms-form (mapv #(expression types %) (:arms node)) (atom-form types (:test node)))
+    :loop (list 'loop* (vec (mapcat (fn [binding]
+                                    [(:target binding) (binding-form types (:target binding)
+                                                         (compact/atom-type types (:value binding))
+                                                         (atom-form types (:value binding)))]) (:bindings node)))
+                (expression types (:body node)))
+    :recur (list* 'recur (mapv #(atom-form types %) (:args node)))
+    :return (let [value (:value node) form (atom-form types value)]
+              ;; Tail compaction must retain the region's opaque result type.
+              (if (and (= :inline (:op value)) (= :native (:op (:value value))))
+                (host/box-form form) form))
+    :throw (list 'throw (atom-form types (:value node)))))
diff --git a/src/com/tailrecursion/blacktide/compiler/transfers.clj b/src/com/tailrecursion/blacktide/compiler/passes/transfers.clj
similarity index 94%
rename from src/com/tailrecursion/blacktide/compiler/transfers.clj
rename to src/com/tailrecursion/blacktide/compiler/passes/transfers.clj
index 3be932b..a64bc28 100644
--- a/src/com/tailrecursion/blacktide/compiler/transfers.clj
+++ b/src/com/tailrecursion/blacktide/compiler/passes/transfers.clj
@@ -1,4 +1,4 @@
-(ns ^:typed.clojure com.tailrecursion.blacktide.compiler.transfers
+(ns ^:typed.clojure com.tailrecursion.blacktide.compiler.passes.transfers
   "Resolve region-local jumps after all labels exist, including dead source.
   Source analysis admits only outer-body labels and statement-position jumps."
   (:require [com.tailrecursion.blacktide.compiler.errors :as errors]
diff --git a/src/com/tailrecursion/blacktide/compiler/value_facts.clj b/src/com/tailrecursion/blacktide/compiler/passes/value_facts.clj
similarity index 68%
rename from src/com/tailrecursion/blacktide/compiler/value_facts.clj
rename to src/com/tailrecursion/blacktide/compiler/passes/value_facts.clj
index 75d5732..dd45f4d 100644
--- a/src/com/tailrecursion/blacktide/compiler/value_facts.clj
+++ b/src/com/tailrecursion/blacktide/compiler/passes/value_facts.clj
@@ -1,23 +1,21 @@
-(ns ^:typed.clojure com.tailrecursion.blacktide.compiler.value-facts
+(ns ^:typed.clojure com.tailrecursion.blacktide.compiler.passes.value-facts
   "Prove primitive results across all reachable definitions, including copy
   cycles. Hints describe consumers; only actual primitive producers prove values."
   (:require [com.tailrecursion.nanopass :as np]
             [com.tailrecursion.blacktide.compiler.languages :as l]
-            [com.tailrecursion.blacktide.compiler.analysis-languages :as a]
             [com.tailrecursion.blacktide.compiler.jvm :as jvm]
-            [com.tailrecursion.blacktide.compiler.jvm-languages :as j]
-            [com.tailrecursion.blacktide.compiler.graph :as graph]
+            [com.tailrecursion.blacktide.compiler.passes.graph :as graph]
             [typed.clojure :as-alias t]))
 
-(np/defpass primitive {:from [j/JVM Type] :to [a/Analysis Type] :body true}
+(np/defpass primitive {:from [l/JVM Type] :to [l/Analysis Type] :body true}
   [type]
   (if (and (class? type) (.isPrimitive ^Class type) (not= Void/TYPE type)) type :boxed))
 
-(np/defpass join {:from [a/Analysis Type] :to [a/Analysis Type] :args [a/Analysis-Type] :body true}
+(np/defpass join {:from [l/Analysis Type] :to [l/Analysis Type] :args [l/Analysis-Type] :body true}
   [a b]
   (cond (= a :bottom) b (= b :bottom) a (= a b) a :else :boxed))
 
-(np/defpass atom-type {:from [l/Machine Atom] :to [a/Analysis Type] :args [a/Analysis-Types]}
+(np/defpass atom-type {:from [l/Machine Atom] :to [l/Analysis Type] :args [l/Analysis-Types]}
   [types node]
   :local (get types (:id node) :bottom)
   ;; Quoted Integer constants must keep their boxed identity and width.
@@ -25,7 +23,7 @@
                  (instance? Double (:value node)) (primitive Double/TYPE)
                  :else :boxed))
 
-(np/defpass rhs-type {:from [l/Machine Rhs] :to [a/Analysis Type] :args [a/Analysis-Types]}
+(np/defpass rhs-type {:from [l/Machine Rhs] :to [l/Analysis Type] :args [l/Analysis-Types]}
   [types rhs]
   :value (atom-type types (:value rhs))
   :external (if (:primitive? (:jvm rhs)) (primitive (:type (:jvm rhs))) :boxed)
@@ -41,25 +39,25 @@
   :invoke :boxed)
 
 (np/defpass definitions
-  {:from [l/Machine Program] :to [a/Analysis Definitions] :args [(t/Set l/Machine-Label)] :body true}
+  {:from [l/Machine Program] :to [l/Analysis Definitions] :args [(t/Set l/Machine-Label)] :body true}
   [reachable machine]
-  (reduce (fn [^{::t/- a/Analysis-Definitions} out ^{::t/- l/Machine-Operation} op]
+  (reduce (fn [^{::t/- l/Analysis-Definitions} out ^{::t/- l/Machine-Operation} op]
             (assoc out (:target op) (conj (get out (:target op) []) (:value op))))
           {} (mapcat :ops (vals (select-keys (:blocks machine) reachable)))))
 
-(np/defpass solve {:from [a/Analysis Definitions] :to [a/Analysis Types] :body true}
+(np/defpass solve {:from [l/Analysis Definitions] :to [l/Analysis Types] :body true}
   [definitions]
-  ((fn ^{::t/- [a/Analysis-Types :-> a/Analysis-Types]} fixed-point [types]
+  ((fn ^{::t/- [l/Analysis-Types :-> l/Analysis-Types]} fixed-point [types]
      (let [next (reduce-kv
-                  (fn [^{::t/- a/Analysis-Types} out ^{::t/- t/Sym} id ^{::t/- (t/Vec l/Machine-Rhs)} values]
+                  (fn [^{::t/- l/Analysis-Types} out ^{::t/- t/Sym} id ^{::t/- (t/Vec l/Machine-Rhs)} values]
                     (assoc out id
-                      ((fn ^{::t/- [t/Int a/Analysis-Type :-> a/Analysis-Type]} common [index type]
+                      ((fn ^{::t/- [t/Int l/Analysis-Type :-> l/Analysis-Type]} common [index type]
                          (if (< index (count values))
                            (recur (inc index) (join type (rhs-type types (nth values index)))) type))
                        0 :bottom))) {} definitions)]
        (if (= types next) next (recur next)))) {}))
 
-(np/defpass analyze {:from [l/Machine Program] :to [a/Analysis Program] :body true}
+(np/defpass analyze {:from [l/Machine Program] :to [l/Analysis Program] :body true}
   [machine]
   (let [reachable (graph/reachable (graph/collect-facts {:entry (:entry machine) :blocks (:blocks machine)}) (:entry machine))]
     {:machine machine :reachable reachable :types (solve (definitions reachable machine))}))
diff --git a/src/com/tailrecursion/blacktide/compiler/prepare.clj b/src/com/tailrecursion/blacktide/compiler/prepare.clj
deleted file mode 100644
index 3d004e4..0000000
--- a/src/com/tailrecursion/blacktide/compiler/prepare.clj
+++ /dev/null
@@ -1,46 +0,0 @@
-(ns ^:typed.clojure com.tailrecursion.blacktide.compiler.prepare
-  "Make function casts and resolved JVM argument conversions explicit before
-  ordering operands. A failing conversion must prevent subsequent evaluation."
-  (:require [com.tailrecursion.nanopass :as np]
-            [com.tailrecursion.blacktide.compiler.source-languages :as l]
-            [com.tailrecursion.blacktide.compiler.jvm-languages :as j]
-            [com.tailrecursion.blacktide.compiler.jvm :as jvm]
-            [typed.clojure :as-alias t]))
-
-(declare prepare prepare-binding)
-
-(np/defpass prepare-forms
-  {:from [l/Collections Exprs] :to [l/Prepared Exprs] :processors {Expr prepare}}
-  [forms])
-
-(np/defpass conversion
-  {:from [l/Prepared Expr] :to [l/Prepared Expr] :args [(t/Option j/JVM-Conversion)] :body true}
-  [conversion expr]
-  (if conversion
-    (let [fact (jvm/fact (.isPrimitive ^Class (:type conversion)) {:hint nil :var-meta {}} (:type conversion))]
-      ;; The consumer still sees Object, but evaluating an Object cast cannot
-      ;; fail. Operand normalization supplies the required value snapshot.
-      (if (= Object (:type conversion)) (assoc expr :jvm fact)
-        (assoc expr :jvm fact :node {:op :convert :conversion conversion :value expr}))) expr))
-
-(np/defpass prepare-node
-  {:from [l/Collections Node] :to [l/Prepared Node]
-   :processors {Expr prepare Exprs prepare-forms Binding prepare-binding Raw clojure.core/identity Local clojure.core/identity
-                CollectionKind clojure.core/identity RecurTarget clojure.core/identity CaseSpec clojure.core/identity}}
-  [node]
-  :invoke {:op :invoke :function (conversion {:type clojure.lang.IFn :parameter? false} (prepare (:function node)))
-           :args (mapv prepare (:args node))}
-  :host
-  (let [plan (jvm/prepare-call (mapv #(:type (:jvm %)) (:args node)) (:operation node))]
-    {:op :host-call :operation (:operation plan)
-     :args (mapv (fn [how value] (conversion how (prepare value))) (:conversions plan) (:args node))}))
-
-(np/defpass prepare-binding
-  {:from [l/Collections Binding] :to [l/Prepared Binding]
-   :processors {Expr prepare Local clojure.core/identity}}
-  [binding])
-
-(np/defpass prepare
-  {:from [l/Collections Expr] :to [l/Prepared Expr] :body true}
-  [expr]
-  (assoc expr :node (prepare-node (:node expr))))
diff --git a/src/com/tailrecursion/blacktide/compiler/source.clj b/src/com/tailrecursion/blacktide/compiler/source.clj
deleted file mode 100644
index 53ea460..0000000
--- a/src/com/tailrecursion/blacktide/compiler/source.clj
+++ /dev/null
@@ -1,412 +0,0 @@
-(ns ^:typed.clojure com.tailrecursion.blacktide.compiler.source
-  "Expand in the caller namespace while discovering lexical bindings. Macro
-  expansion and binding discovery stay ordered; every child becomes bound IR."
-  (:require [com.tailrecursion.blacktide.compiler.errors :as errors]
-            [com.tailrecursion.nanopass :as np]
-            [com.tailrecursion.blacktide.compiler.source-languages :as l]
-            [typed.clojure :as-alias t]
-            [com.tailrecursion.blacktide.compiler.jvm-languages :as j]
-            [com.tailrecursion.blacktide.compiler.jvm :as jvm]
-            [com.tailrecursion.blacktide.compiler.source-jvm :as facts]
-            [com.tailrecursion.blacktide.compiler.loop-facts :as loops]
-            [com.tailrecursion.blacktide.compiler.source-case :as cases]
-            [com.tailrecursion.blacktide.compiler.effects :refer [effects]]
-            [com.tailrecursion.blacktide.compiler.source-host :as host]))
-
-(declare parse parse-forms parse-list parse-bindings)
-
-(np/defpass fresh
-  {:from [l/Bound Supply] :to (t/HVec [l/Bound-Supply t/Sym]) :args [t/Str] :body true}
-  [stem supply]
-  ;; Put the unique counter before the user-controlled stem. A source name
-  ;; must not impersonate another binding's generated identity.
-  [(assoc supply :next (inc (:next supply))) (symbol (str (:prefix supply) "__" (:next supply) "__" stem))])
-
-(np/defpass expression
-  {:from [l/Bound Node] :to [l/Bound Expr] :args [l/Bound-Context j/JVM-Seed] :body true}
-  [ctx jvm node]
-  {:site (:site ctx) :jvm (facts/infer jvm node) :node node})
-
-(np/defpass resolve-name
-  {:from [l/Bound Raw] :to (t/U nil t/AnyVar java.lang.Class) :args [l/Bound-Context] :body true}
-  [ctx name]
-  (if (var? name) name
-      (when (and (symbol? name) (not (contains? (:locals ctx) name))) (ns-resolve *ns* name))))
-
-(np/defpass parse-symbol
-  {:from [l/Bound Raw] :to [l/Bound Expr] :args [l/Bound-Context] :body true}
-  [ctx form]
-  (if (symbol? form)
-    (if-let [local (get (:locals ctx) form)]
-      (expression ctx {:hint (jvm/source-tag form) :var-meta {}} {:op :local :local local})
-      (let [resolved (resolve-name ctx form) qualified (host/qualified form)]
-        (cond
-          (var? resolved)
-          (let [m (or (meta resolved) {})]
-            (errors/check-visibility resolved (:site ctx))
-            (when (:macro m) (errors/fail :invalid-expression "Cannot take the value of a macro" {} (:site ctx)))
-            (if (:const m)
-              (expression ctx {:hint nil :var-meta {}} {:op :literal :value (.get ^clojure.lang.Var resolved)})
-              (expression ctx {:hint (jvm/source-tag form) :var-meta m}
-                          {:op :global :name (.toSymbol ^clojure.lang.Var resolved)})))
-          (class? resolved) (expression ctx {:hint (jvm/source-tag form) :var-meta {}} {:op :literal :value resolved})
-          qualified
-          (let [operation qualified]
-            (expression ctx {:hint (jvm/source-tag form) :var-meta {}}
-                        {:op :host :operation (if (= :static-field (:op operation)) operation
-                                                 {:op :method-value :name form}) :args []}))
-          :else (errors/fail :unresolved-symbol "Unable to resolve symbol" {:binding form} (:site ctx)))))
-    (errors/fail :invalid-expression "Expected a symbol" {} (:site ctx))))
-
-(np/defpass parse-operand
-  {:from [l/Bound Raw] :to (t/HVec [l/Bound-Supply l/Bound-Expr])
-   :args [l/Bound-Supply l/Bound-Context] :body true}
-  [supply ctx form]
-  (parse supply (assoc ctx :statement? false :tail? false :outer? false :transfer? false) form))
-
-(np/defpass parse-forms
-  {:from [l/Bound Forms] :to (t/HVec [l/Bound-Supply (t/Vec l/Bound-Expr)])
-   :args [l/Bound-Supply l/Bound-Context] :body true}
-  [supply ctx forms]
-  (reduce (fn [^{::t/- (t/HVec [l/Bound-Supply (t/Vec l/Bound-Expr)])} acc ^{::t/- l/Bound-Raw} form]
-            (let [[supply out] acc
-                  last? (= (count out) (dec (count forms)))
-                  ctx (assoc ctx :tail? (and (:tail? ctx) last?))
-                  [supply expr] (if (:statement? ctx) (parse supply ctx form) (parse-operand supply ctx form))]
-              [supply (conj out expr)]))
-          [supply []] forms))
-
-(np/defpass parse-body
-  {:from [l/Bound Forms] :to (t/HVec [l/Bound-Supply l/Bound-Expr])
-   :args [l/Bound-Supply l/Bound-Context] :body true}
-  [supply ctx forms]
-  (let [[supply forms] (parse-forms supply (assoc ctx :statement? true) forms)]
-    [supply (expression ctx {:hint nil :var-meta {}} {:op :do :forms forms})]))
-
-(np/defpass bind-local
-  {:from [l/Bound Raw] :to (t/HVec [l/Bound-Supply l/Bound-Context l/Bound-Local])
-   :args [l/Bound-Supply l/Bound-Context t/Bool j/JVM-Fact] :body true}
-  [supply ctx mutable? init name]
-  (if (simple-symbol? name)
-    (let [[supply id] (fresh (str name) supply)
-          jvm (facts/binding-facts name mutable? init)
-          type (:type jvm)
-          tag (or (:hint jvm) (when (class? type) (symbol (.getName ^Class type))))
-          local {:id id :name name :mutable? mutable? :external? false
-                 :jvm jvm}]
-      [supply (assoc ctx :locals (assoc (:locals ctx) name local)
-                         :macro-env (assoc (:macro-env ctx) name {:name name :tag tag})) local])
-    (errors/fail :invalid-binding "Binding targets must be simple symbols" {:binding name} (:site ctx))))
-
-(np/defpass parse-bindings
-  {:from [l/Bound Raw] :to (t/HVec [l/Bound-Supply l/Bound-Context (t/Vec l/Bound-Binding)])
-   :args [l/Bound-Supply l/Bound-Context (t/U ':mutable ':immutable ':loop)] :body true}
-  [supply ctx mode bindings]
-  (if (and (vector? bindings) (even? (count bindings)))
-    (reduce (fn [^{::t/- (t/HVec [l/Bound-Supply l/Bound-Context (t/Vec l/Bound-Binding)])} acc
-                 ^{::t/- (t/Seq t/Any)} pair]
-              (let [[supply ctx out] acc
-                    [supply init] (parse-operand supply ctx (second pair))
-                    [supply ctx local] (bind-local supply ctx (not= mode :immutable) (:jvm init) (first pair))
-                    local (cond-> local (= mode :loop) (assoc :mutable? false))
-                    ctx (assoc ctx :locals (assoc (:locals ctx) (:name local) local))
-                    hint (jvm/tag-class (:hint (:jvm local)))
-                    init (if (and hint (= mode :immutable))
-                           (expression ctx {:hint nil :var-meta {}} {:op :host :operation {:op :adapt :class hint} :args [init]}) init)]
-                [supply ctx (conj out {:local local :init init})])) [supply ctx []] (partition 2 bindings))
-    (errors/fail :invalid-bindings "Expected an even binding vector" {} (:site ctx))))
-
-(np/defpass parse-scope
-  {:from [l/Bound Forms] :to (t/HVec [l/Bound-Supply l/Bound-Expr])
-   :args [l/Bound-Supply l/Bound-Context t/Bool] :body true}
-  [supply ctx mutable? forms]
-  (let [inner (assoc ctx :outer? mutable?)
-        [supply inner bindings] (parse-bindings supply inner (if mutable? :mutable :immutable) (second forms))
-        [supply body] (parse-body supply inner (vec (drop 2 forms)))]
-    [supply (expression ctx {:hint (jvm/source-tag (:form (:site ctx))) :var-meta {}}
-                        {:op :let :bindings bindings :body body})]))
-
-(np/defpass parse-loop
-  {:from [l/Bound Forms] :to (t/HVec [l/Bound-Supply l/Bound-Expr])
-   :args [l/Bound-Supply l/Bound-Context] :body true}
-  [supply ctx forms]
-  (let [[supply id] (fresh "loop" supply)
-        inner (assoc ctx :outer? false :recur nil :tail? false)
-        [supply inner bindings] (parse-bindings supply inner :loop (second forms))
-        target {:id id :bindings (mapv :local bindings)}
-        [supply body] (parse-body supply (assoc inner :recur target :tail? true) (vec (drop 2 forms)))]
-    [supply (first (loops/solve {} (expression ctx {:hint nil :var-meta {}} {:op :loop :id id :bindings bindings :body body})))]))
-
-(np/defpass parse-recur
-  {:from [l/Bound Forms] :to (t/HVec [l/Bound-Supply l/Bound-Expr])
-   :args [l/Bound-Supply l/Bound-Context] :body true}
-  [supply ctx forms]
-  (let [target (:recur ctx)]
-    (when-not (and target (:tail? ctx))
-      (errors/fail :invalid-recur "recur requires the tail position of a local loop" {} (:site ctx)))
-    (when-not (= (count (:bindings target)) (dec (count forms)))
-      (errors/fail :recur-arity "recur argument count does not match its target" {} (:site ctx)))
-    (let [[supply args] (parse-forms supply (assoc ctx :statement? false :tail? false) (subvec forms 1))]
-      [supply (expression ctx {:hint nil :var-meta {}} {:op :recur :target target :args args})])))
-
-(np/defpass parse-arms
-  {:from [l/Bound Forms] :to (t/HVec [l/Bound-Supply l/Bound-Exprs])
-   :args [l/Bound-Supply l/Bound-Context] :body true}
-  [supply ctx forms]
-  (reduce (fn [^{::t/- (t/HVec [l/Bound-Supply l/Bound-Exprs])} acc ^{::t/- t/Any} form]
-            (let [[supply arms] acc [supply arm] (parse supply (assoc ctx :outer? false) form)] [supply (conj arms arm)]))
-          [supply []] forms))
-
-(np/defpass case-expression
-  {:from [l/Bound Exprs] :to (t/HVec [l/Bound-Supply l/Bound-Expr])
-   :args [l/Bound-Supply l/Bound-Context l/Bound-Expr] :body true}
-  [supply ctx selector arms]
-  [supply (expression ctx {:hint nil :var-meta {}} {:op :case :selector selector :arms arms})])
-
-(np/defpass parse-case
-  {:from [l/Bound Forms] :to (t/HVec [l/Bound-Supply l/Bound-Expr])
-   :args [l/Bound-Supply l/Bound-Context] :body true}
-  [supply ctx forms]
-  (let [{:keys [selector arms]} (cases/expand-case (:macro-env ctx) forms)]
-    (if (empty? arms) (parse supply ctx selector)
-      (do
-        (when-not (and (seq? selector) (= 3 (count selector)))
-          (errors/fail :invalid-case "Expected core case's selector binding" {} (:site ctx)))
-        (let [[supply inner bindings] (parse-bindings supply ctx :immutable (second selector))
-              [supply selector] (parse-operand supply inner (first (nnext selector)))
-              [supply arms] (parse-arms supply inner arms)
-              [supply body] (case-expression supply ctx selector arms)]
-          [supply (expression ctx {:hint nil :var-meta {}} {:op :let :bindings bindings :body body})])))))
-
-(np/defpass parse-dispatch
-  {:from [l/Bound Forms] :to (t/HVec [l/Bound-Supply l/Bound-Expr])
-   :args [l/Bound-Supply l/Bound-Context] :body true}
-  [supply ctx forms]
-  (let [{:keys [value spec arms]} (cases/dispatch-syntax forms)
-        [supply value] (parse-operand supply ctx value)]
-    (when-not (= :local (:op (:node value)))
-      (errors/fail :invalid-case "case* dispatch must reference a lexical local" {} (:site ctx)))
-    (let [selector (expression ctx {:hint nil :var-meta {}} {:op :dispatch :value value :spec spec})
-          [supply arms] (parse-arms supply ctx arms)]
-      (case-expression supply ctx selector arms))))
-
-(np/defpass protocol-argument
-  {:from [l/Bound Expr] :to (t/HVec [l/Bound-Supply l/Bound-Expr])
-   :args [l/Bound-Supply l/Bound-Context l/Bound-Expr java.lang.Class] :body true}
-  [supply ctx direct? type arg]
-  (let [seed {:hint nil :var-meta {}} [supply id] (fresh "protocol-argument" supply)
-        local {:id id :name id :mutable? false :external? false :jvm (facts/binding-facts id false (:jvm arg))}
-        read (expression ctx seed {:op :local :local local})
-        boxed (expression ctx seed {:op :host :operation {:op :static :class clojure.lang.RT :member 'box :tags [Object]} :args [read]})
-        converted (if (.isPrimitive ^Class type)
-                    (expression ctx seed {:op :host :operation {:op :static :class clojure.lang.RT :member 'box :tags [type]} :args [read]})
-                    (expression ctx seed {:op :host :operation {:op :instance :class Class :member 'cast :mode :method :tags [Object]}
-                                         :args [(expression ctx seed {:op :literal :value type})
-                                                (expression ctx seed {:op :host :operation {:op :adapt :class type} :args [read]})]}))]
-    [supply (expression ctx seed {:op :let :bindings [{:local local :init arg}]
-                                 :body (expression ctx seed {:op :if :test direct? :then converted :else boxed})})]))
-
-(np/defpass protocol-call
-  {:from [l/Bound Exprs] :to (t/HVec [l/Bound-Supply l/Bound-Expr])
-   :args [l/Bound-Supply l/Bound-Context j/JVM-Seed j/JVM-Protocol t/Sym] :body true}
-  [supply ctx seed method name args]
-  (let [plain {:hint nil :var-meta {}} [supply id] (fresh "protocol-receiver" supply)
-        receiver (expression ctx plain {:op :host :operation {:op :static :class clojure.lang.RT :member 'box :tags [Object]} :args [(nth args 0)]})
-        local {:id id :name id :mutable? false :external? false :jvm (:jvm receiver)}
-        read (expression ctx plain {:op :local :local local})
-        direct? (if-let [class (:class method)]
-                  (expression ctx plain {:op :host :operation {:op :instanceof :class class} :args [read]})
-                  (expression ctx plain {:op :literal :value false}))
-        root (expression ctx plain {:op :host :operation {:op :instance :class clojure.lang.Var :member 'getRawRoot :mode :method :tags []}
-                                    :args [(expression ctx plain {:op :var :name name})]})
-        root (expression ctx plain {:op :if :test direct? :then (expression ctx plain {:op :literal :value nil}) :else root})
-        [supply args] (reduce (fn [^{::t/- (t/HVec [l/Bound-Supply l/Bound-Exprs])} acc ^{::t/- t/Int} index]
-                               (let [[supply out] acc arg (nth args index) type (get (:parameters method) (dec index) Object)
-                                     [supply arg] (if (= Object type) [supply arg] (protocol-argument supply ctx direct? type arg))]
-                                 [supply (conj out arg)])) [supply [read root]] (range 1 (count args)))]
-    [supply (expression ctx seed {:op :let :bindings [{:local local :init receiver}]
-                                 :body (expression ctx seed {:op :host :operation {:op :protocol :method method} :args args})})]))
-
-(np/defpass keyword-call
-  {:from [l/Bound Expr] :to (t/HVec [l/Bound-Supply l/Bound-Expr])
-   :args [l/Bound-Supply l/Bound-Context j/JVM-Seed t/Kw] :body true}
-  [supply ctx seed key target]
-  (let [plain {:hint nil :var-meta {}} [supply id] (fresh "keyword-site" supply)
-        site (expression ctx plain {:op :host :operation {:op :keyword-site :key key} :args []})
-        local {:id id :name id :mutable? false :external? false :jvm (:jvm site)}
-        read (expression ctx plain {:op :local :local local})
-        snapshot (expression ctx plain {:op :host :operation {:op :instance :class com.tailrecursion.blacktide.runtime.KeywordSite
-                                                              :member 'snapshot :mode :method :tags []} :args [read]})]
-    ;; The first argument snapshots the cache before the target runs.
-    ;; An IFn call retains KeywordInvokeExpr's unknown result type.
-    [supply (expression ctx seed {:op :let :bindings [{:local local :init site}]
-                                 :body (expression ctx seed {:op :invoke :function read :args [snapshot target]})})]))
-
-(np/defpass parse-call
-  {:from [l/Bound Forms] :to (t/HVec [l/Bound-Supply l/Bound-Expr])
-   :args [l/Bound-Supply l/Bound-Context] :body true}
-  [supply ctx forms]
-  (let [inner (assoc ctx :statement? false :tail? false)
-        [supply function] (parse-operand supply inner (first forms))
-        arity (dec (count forms))
-        direct (facts/direct-call arity function)
-        metadata (:var-meta (:jvm function))
-        primitive (when-not direct (facts/primitive-interface arity (:arglists metadata)))
-        hint (or (jvm/source-tag (:form (:site ctx)))
-                 (jvm/source-tag (facts/signature arity (:arglists metadata)))
-                 (when (and (not direct) (not primitive) (= :global (:op (:node function)))) (:hint (:jvm function)))
-                 (:tag metadata))
-        [supply args] (parse-forms supply inner (vec (rest forms)))
-        class-arg (some-> args first :node)
-        node (cond
-               (and (= :global (:op (:node function))) (= 'clojure.core/instance? (:name (:node function)))
-                    (= 2 (count args)) (= :literal (:op class-arg)) (class? (:value class-arg)))
-               {:op :host :operation {:op :instanceof :class (:value class-arg)} :args [(nth args 1)]}
-               direct {:op :host :operation direct :args args}
-               primitive {:op :host :operation {:op :instance :class primitive :member 'invokePrim :mode :method :tags nil}
-                          :args (into [function] args)}
-               :else {:op :invoke :function function :args args})]
-    (let [method (when-not (or direct primitive) (facts/protocol-call arity function))
-          fnode (:node function) key (:form (:site function))]
-      (cond
-        (and method (= :global (:op fnode)))
-        (protocol-call supply ctx {:hint hint :var-meta {}} method (:name fnode) args)
-        (and (= :literal (:op fnode)) (keyword? key) (= 1 arity)
-             (effects (nth args 0)))
-        (keyword-call supply ctx {:hint hint :var-meta {}} key (nth args 0))
-        :else [supply (expression ctx {:hint hint :var-meta {}} node)]))))
-
-(np/defpass parse-host
-  {:from [j/JVM Syntax] :to (t/HVec [l/Bound-Supply l/Bound-Expr])
-   :args [l/Bound-Supply l/Bound-Context] :body true}
-  [supply ctx syntax]
-  (let [[supply args] (parse-forms supply (assoc ctx :statement? false :tail? false) (:operands syntax))]
-    [supply (expression ctx {:hint (jvm/source-tag (:form (:site ctx))) :var-meta {}}
-                        {:op :host :operation (:operation syntax) :args args})]))
-
-(np/defpass parse-assignment
-  {:from [l/Bound Forms] :to (t/HVec [l/Bound-Supply l/Bound-Expr])
-   :args [l/Bound-Supply l/Bound-Context (t/U ':setq ':psetq)] :body true}
-  [supply ctx kind forms]
-  (if (even? (count forms))
-    (let [[supply pairs]
-          (reduce (fn [^{::t/- (t/HVec [l/Bound-Supply l/Bound-Assignments])} acc ^{::t/- (t/Seq t/Any)} pair]
-                    (let [[supply pairs] acc target (first pair)]
-                      (when-not (simple-symbol? target)
-                        (errors/fail :invalid-assignment "Assignment targets must be simple symbols" {:binding target} (:site ctx)))
-                      (let [local (or (get (:locals ctx) target)
-                                      (errors/fail :unknown-assignment "Expected a mutable loophole local" {:binding target} (:site ctx)))]
-                        (when-not (:mutable? local)
-                          (errors/fail :immutable-assignment "Cannot assign to an immutable local" {:binding target} (:site ctx)))
-                        (let [[supply value] (parse-operand supply ctx (second pair))]
-                          [supply (conj pairs {:target local :value value})])))) [supply []] (partition 2 forms))]
-      [supply (expression ctx {:hint (jvm/source-tag (:form (:site ctx))) :var-meta {}}
-                          (if (= kind :setq) {:op :setq :pairs pairs} {:op :psetq :pairs pairs}))])
-    (errors/fail :invalid-assignment "setq/psetq requires symbol/value pairs" {} (:site ctx))))
-
-(np/defpass parse-marker
-  {:from [l/Bound Forms] :to [l/Bound Expr] :args [l/Bound-Context (t/U ':label ':goto)] :body true}
-  [ctx kind forms]
-  (let [name (second forms)]
-    (when-not (and (= 2 (count forms)) (simple-symbol? name))
-      (errors/fail :invalid-marker "label/goto requires one simple symbol" {:label name} (:site ctx)))
-    (when-not (and (:statement? ctx) (:transfer? ctx) (or (= kind :goto) (:outer? ctx)))
-      (errors/fail :marker-position "Labels require the outer body; goto requires a statement outside operands and initializers"
-                   {:operator kind :label name} (:site ctx)))
-    (expression ctx {:hint nil :var-meta {}}
-                (if (= kind :label) {:op :label :name name}
-                    {:op :goto :name name}))))
-
-(np/defpass parse-list
-  {:from [l/Bound Forms] :to (t/HVec [l/Bound-Supply l/Bound-Expr])
-   :args [l/Bound-Supply l/Bound-Context] :body true}
-  [supply ctx forms]
-  (let [op (first forms) form (:form (:site ctx)) resolved (resolve-name ctx op)
-        marker (when (var? resolved) (get (:markers ctx) resolved))
-        _ (when (var? resolved) (errors/check-visibility resolved (:site ctx)))]
-    (cond
-      (= :loophole marker)
-      (errors/fail :unsupported-form "Nested loophole regions are unsupported" {:operator op} (:site ctx))
-      (= :setq marker) (parse-assignment supply ctx :setq (vec (rest forms)))
-      (= :psetq marker) (parse-assignment supply ctx :psetq (vec (rest forms)))
-      (= :label marker) [supply (parse-marker ctx :label forms)]
-      (= :goto marker) [supply (parse-marker ctx :goto forms)]
-      (= op 'quote)
-      (if (= 2 (count forms))
-        [supply (expression ctx {:hint nil :var-meta {}} {:op :literal :value (second forms)})]
-        (errors/fail :invalid-quote "quote requires one argument" {} (:site ctx)))
-      (= op 'var)
-      (let [name (second forms) resolved (when (symbol? name) (ns-resolve *ns* name))]
-        (if (and (= 2 (count forms)) (var? resolved))
-          [supply (expression ctx {:hint nil :var-meta {}}
-                              {:op :var :name (.toSymbol ^clojure.lang.Var resolved)})]
-          (errors/fail :invalid-var "var requires a resolvable Var name" {} (:site ctx))))
-      (#{'fn* 'letfn* 'try 'set! 'monitor-enter 'monitor-exit} op)
-      (errors/fail :unsupported-form "Form is unsupported inside loophole; place it outside the region" {:operator op} (:site ctx))
-      (#{'new '.} op) (parse-host supply ctx (or (host/syntax ctx forms)
-                                                (errors/fail :invalid-interop "Missing host syntax" {} (:site ctx))))
-      (= op 'do) (parse-body supply ctx (vec (rest forms)))
-      (= op 'let*) (parse-scope supply ctx false forms)
-      (= op 'loop*) (parse-loop supply ctx forms)
-      (= op 'recur) (parse-recur supply ctx forms)
-      (= op 'case*) (parse-dispatch supply ctx forms)
-      (and (= resolved #'clojure.core/case) (< (count forms) 2))
-      (errors/fail :invalid-case "case requires a dispatch expression" {} (:site ctx))
-      (= resolved #'clojure.core/case) (parse-case supply ctx forms)
-      (= op 'if)
-      (if (<= 3 (count forms) 4)
-        (let [[supply test] (parse-operand supply ctx (second forms))
-              [supply yes] (parse supply (assoc ctx :outer? false) (nth forms 2))
-              [supply no] (parse supply (assoc ctx :outer? false) (get forms 3))]
-          [supply (expression ctx {:hint (jvm/source-tag form) :var-meta {}} {:op :if :test test :then yes :else no})])
-        (errors/fail :invalid-if "if requires a test, then, and optional else" {} (:site ctx)))
-      (= op 'throw)
-      (if (= 2 (count forms))
-        (let [[supply value] (parse-operand supply ctx (second forms))]
-          [supply (expression ctx {:hint nil :var-meta {}} {:op :throw :value value})])
-        (errors/fail :invalid-throw "throw requires one value" {} (:site ctx)))
-      (and (symbol? op) (special-symbol? op))
-      (errors/fail :unsupported-form "Special form is not supported inside loophole" {:operator op} (:site ctx))
-      (and (var? resolved) (:macro (meta resolved)))
-      (let [expanded (.applyTo ^clojure.lang.Var resolved (cons form (cons (:macro-env ctx) (rest forms))))
-            expanded (if (instance? clojure.lang.IObj expanded)
-                       (with-meta expanded (merge (meta expanded) (meta form))) expanded)]
-        (parse supply ctx expanded))
-      (and (var? resolved) (ifn? (:inline (meta resolved)))
-           (let [arity (:inline-arities (meta resolved))] (or (nil? arity) (and (ifn? arity) (.invoke ^clojure.lang.IFn arity (dec (count forms)))))))
-      (let [inline (:inline (meta resolved))
-            expanded (if (ifn? inline) (.applyTo ^clojure.lang.IFn inline (rest forms))
-                         (errors/fail :invalid-expression "Invalid inline expander" {} (:site ctx)))
-            expanded (if (instance? clojure.lang.IObj expanded)
-                       (with-meta expanded (merge (meta expanded) (meta form))) expanded)]
-        (parse supply ctx expanded))
-      :else (if-let [syntax (host/syntax ctx forms)]
-              (parse-host supply ctx syntax)
-              (parse-call supply ctx forms)))))
-
-(np/defpass parse-collection
-  {:from [l/Bound Raw] :to (t/HVec [l/Bound-Supply l/Bound-Expr])
-   :args [l/Bound-Supply l/Bound-Context] :body true}
-  [supply ctx form]
-  (if (or (vector? form) (map? form) (set? form))
-    (let [kind (cond (vector? form) :vector (map? form) :map :else :set)
-          ;; Freeze source traversal before alpha-renaming changes element hashes.
-          items (if (map? form) (vec (mapcat identity form)) (vec form))
-          inner (assoc ctx :statement? false :tail? false)
-          [supply items] (parse-forms supply inner items)
-          [supply metadata] (if (some? (meta form)) (parse-operand supply inner (meta form)) [supply nil])]
-      [supply (expression ctx {:hint (jvm/source-tag form) :var-meta {}}
-                          {:op :collection :kind kind :items items :metadata metadata})])
-    (errors/fail :invalid-expression "Expected a collection" {} (:site ctx))))
-
-(np/defpass parse
-  {:from [l/Bound Raw] :to (t/HVec [l/Bound-Supply l/Bound-Expr])
-   :args [l/Bound-Supply l/Bound-Context] :body true}
-  [supply ctx form]
-  (let [ctx (assoc ctx :site (errors/site (:site ctx) form))]
-    (cond
-      (symbol? form) [supply (parse-symbol ctx form)]
-      (and (seq? form) (seq form)) (parse-list supply ctx (vec form))
-      (or (vector? form) (map? form) (set? form))
-      (parse-collection supply ctx form)
-      :else [supply (expression ctx {:hint (jvm/source-tag form) :var-meta {}} {:op :literal :value form})])))
diff --git a/src/com/tailrecursion/blacktide/compiler/source_languages.clj b/src/com/tailrecursion/blacktide/compiler/source_languages.clj
deleted file mode 100644
index fc43c9c..0000000
--- a/src/com/tailrecursion/blacktide/compiler/source_languages.clj
+++ /dev/null
@@ -1,93 +0,0 @@
-(ns com.tailrecursion.blacktide.compiler.source-languages
-  "Bound source separates executable children from source/provenance data.
-  Raw forms, macro environments and JVM hints are external compiler inputs."
-  (:require [com.tailrecursion.nanopass :as np]
-            [typed.clojure :as-alias t]
-            [com.tailrecursion.blacktide.compiler.jvm-languages :as j]))
-
-(np/deflanguage Bound
-  {Raw t/Any
-   Forms (t/Vec Raw)
-   Site (t/HMap :mandatory {:form Raw :file (t/Option t/Str)
-                            :line (t/Option t/Int) :column (t/Option t/Int)} :complete? true)
-   Jvm j/JVM-Fact
-   Local (t/HMap :mandatory {:id t/Sym :name t/Sym :mutable? t/Bool :external? t/Bool
-                             :jvm Jvm} :complete? true)
-   Binding (t/HMap :mandatory {:local Local :init Expr} :complete? true)
-   Bindings (t/Vec Binding)
-   Assignment (t/HMap :mandatory {:target Local :value Expr} :complete? true)
-   Assignments (t/Vec Assignment)
-   RecurTarget (t/HMap :mandatory {:id t/Sym :bindings (t/Vec Local)} :complete? true)
-   CaseSpec (t/HMap :mandatory {:shift t/Int :mask t/Int :default t/Int
-                                :tests (t/Map t/Int (t/HVec [Raw t/Int]))
-                                :switch (t/U ':compact ':sparse)
-                                :test (t/U ':int ':hash-equiv ':hash-identity)
-                                :skip (t/Option (t/Set t/Int))} :complete? true)
-   CaseSyntax (t/HMap :mandatory {:selector Raw :arms Forms} :complete? true)
-   DispatchSyntax (t/HMap :mandatory {:value Raw :spec CaseSpec :arms Forms} :complete? true)
-   Exprs (t/Vec Expr)
-   CollectionKind (t/U ':vector ':map ':set)
-   Expr (t/HMap :mandatory {:site Site :jvm Jvm :node Node} :complete? true)
-   Node (t/U (t/HMap :mandatory {:op ':literal :value Raw} :complete? true)
-             (t/HMap :mandatory {:op ':local :local Local} :complete? true)
-             (t/HMap :mandatory {:op ':global :name t/Sym} :complete? true)
-             (t/HMap :mandatory {:op ':var :name t/Sym} :complete? true)
-             (t/HMap :mandatory {:op ':host :operation j/JVM-Operation :args (t/Vec Expr)} :complete? true)
-             (t/HMap :mandatory {:op ':invoke :function Expr :args (t/Vec Expr)} :complete? true)
-             (t/HMap :mandatory {:op ':collection :kind CollectionKind :items Exprs
-                                 :metadata (t/Option Expr)} :complete? true)
-             (t/HMap :mandatory {:op ':setq :pairs Assignments} :complete? true)
-             (t/HMap :mandatory {:op ':psetq :pairs Assignments} :complete? true)
-             (t/HMap :mandatory {:op ':if :test Expr :then Expr :else Expr} :complete? true)
-             (t/HMap :mandatory {:op ':case :selector Expr :arms Exprs} :complete? true)
-             (t/HMap :mandatory {:op ':dispatch :value Expr :spec CaseSpec} :complete? true)
-             (t/HMap :mandatory {:op ':throw :value Expr} :complete? true)
-             (t/HMap :mandatory {:op ':label :name t/Sym} :complete? true)
-             (t/HMap :mandatory {:op ':goto :name t/Sym} :complete? true)
-             (t/HMap :mandatory {:op ':do :forms (t/Vec Expr)} :complete? true)
-             (t/HMap :mandatory {:op ':let :bindings (t/Vec Binding) :body Expr} :complete? true)
-             (t/HMap :mandatory {:op ':loop :id t/Sym :bindings (t/Vec Binding) :body Expr} :complete? true)
-             (t/HMap :mandatory {:op ':recur :target RecurTarget :args (t/Vec Expr)} :complete? true))
-   Supply (t/HMap :mandatory {:prefix t/Str :next t/Int} :complete? true)
-   Context (t/HMap :mandatory {:locals (t/Map t/Sym Local) :macro-env (t/Map t/Sym Raw)
-                               :markers (t/Map t/AnyVar t/Kw) :outer? t/Bool :transfer? t/Bool
-                               :site Site
-                               :recur (t/Option RecurTarget) :statement? t/Bool :tail? t/Bool} :complete? true)})
-
-(np/derive-language Native Bound
-  {:remove {Node #{:setq :psetq :label :goto}}})
-
-(np/derive-language Classified Bound
-  {:add {Node [(t/HMap :mandatory {:op ':native :value com.tailrecursion.blacktide.compiler.source-languages/Native-Expr}
-                       :complete? true)]}})
-
-(np/derive-language Sequential Classified
-  {:remove {Node #{:setq}}
-   :add {Node [(t/HMap :mandatory {:op ':write :target Local :value Expr} :complete? true)]}})
-
-(np/derive-language Assigned Sequential
-  {:remove {Node #{:psetq}}})
-
-(np/derive-language Cases Assigned
-  {:remove {Node #{:case}}
-   :add {Node [(t/HMap :mandatory {:op ':select :selector Expr :arms Exprs} :complete? true)]}})
-
-(np/derive-language Collections Cases
-  {:remove {Node #{:collection}}
-   :add {Node [(t/HMap :mandatory {:op ':build :kind CollectionKind :items Exprs :unique? t/Bool
-                                  :metadata (t/Option Expr)} :complete? true)]}})
-
-(np/derive-language Prepared Collections
-  {:remove {Node #{:host}}
-   :add {Node [(t/HMap :mandatory {:op ':host-call :operation j/JVM-Operation :args (t/Vec Expr)} :complete? true)
-                (t/HMap :mandatory {:op ':convert :conversion j/JVM-Conversion :value Expr} :complete? true)]}})
-
-(np/derive-language Ordered Prepared
-  {:remove {Node #{:invoke :build :host-call :convert :recur :dispatch}}
-   :add {Node [(t/HMap :mandatory {:op ':convert :conversion j/JVM-Conversion :value Local} :complete? true)
-                (t/HMap :mandatory {:op ':dispatch :value Local :spec CaseSpec} :complete? true)
-                (t/HMap :mandatory {:op ':recur :target RecurTarget :args (t/Vec Local)} :complete? true)
-                (t/HMap :mandatory {:op ':host-call :operation j/JVM-Operation :args (t/Vec Local)} :complete? true)
-                (t/HMap :mandatory {:op ':invoke :function Local :args (t/Vec Local)} :complete? true)
-                (t/HMap :mandatory {:op ':build :kind CollectionKind :items (t/Vec Local) :unique? t/Bool} :complete? true)
-                (t/HMap :mandatory {:op ':metadata :value Local :metadata Local} :complete? true)]}})
diff --git a/src/com/tailrecursion/blacktide/compiler/source_values.clj b/src/com/tailrecursion/blacktide/compiler/source_values.clj
deleted file mode 100644
index 52713ab..0000000
--- a/src/com/tailrecursion/blacktide/compiler/source_values.clj
+++ /dev/null
@@ -1,36 +0,0 @@
-(ns ^:typed.clojure com.tailrecursion.blacktide.compiler.source-values
-  "Restore primitive source-local reads before compiler temporaries exist.
-  Clojure boxes each Object use of a primitive local independently; a machine
-  slot must not turn those reads into aliases of one box. Native trees already
-  preserve this behavior, and mutable locals have reference semantics."
-  (:require [com.tailrecursion.nanopass :as np]
-            [com.tailrecursion.blacktide.compiler.source-languages :as s]
-            [typed.clojure :as-alias t]))
-
-(declare expression forms binding-form pair pairs)
-
-(np/defpass node
-  {:from [s/Classified Node] :to [s/Classified Node]
-   :processors {Expr expression Exprs forms Binding binding-form Assignments pairs
-                Local clojure.core/identity Raw clojure.core/identity
-                CollectionKind clojure.core/identity RecurTarget clojure.core/identity CaseSpec clojure.core/identity}}
-  [node]
-  :native node)
-
-(np/defpass binding-form {:from [s/Classified Binding] :to [s/Classified Binding]
-                          :processors {Expr expression Local clojure.core/identity}} [binding])
-(np/defpass pair {:from [s/Classified Assignment] :to [s/Classified Assignment]
-                  :processors {Expr expression Local clojure.core/identity}} [assignment])
-(np/defpass pairs {:from [s/Classified Assignments] :to [s/Classified Assignments]
-                   :processors {Assignment pair}} [assignments])
-(np/defpass forms {:from [s/Classified Exprs] :to [s/Classified Exprs] :processors {Expr expression}} [exprs])
-
-(np/defpass expression {:from [s/Classified Expr] :to [s/Classified Expr] :body true}
-  [expr]
-  (let [expr (assoc expr :node (node (:node expr)))
-        type (:type (:jvm expr))]
-    (if (and (= :local (:op (:node expr))) (:primitive? (:jvm expr))
-             (class? type) (.isPrimitive ^Class type) (not= Void/TYPE type))
-      (assoc expr :node {:op :host :operation {:op :static :class clojure.lang.RT
-                                             :member (symbol (str (.getName ^Class type) "Cast")) :tags [type]}
-                         :args [expr]}) expr)))
diff --git a/src/com/tailrecursion/blacktide/compiler/structure.clj b/src/com/tailrecursion/blacktide/compiler/structure.clj
deleted file mode 100644
index 5a98727..0000000
--- a/src/com/tailrecursion/blacktide/compiler/structure.clj
+++ /dev/null
@@ -1,163 +0,0 @@
-(ns ^:typed.clojure com.tailrecursion.blacktide.compiler.structure
-  "Recover acyclic control or a single-header loop with bounded duplication.
-  Construction records block identities to preserve native construction and
-  cache sites. Dead native code keeps the dispatcher for source validation."
-  (:require [com.tailrecursion.nanopass :as np]
-            [com.tailrecursion.blacktide.compiler.languages :as l]
-            [com.tailrecursion.blacktide.compiler.analysis-languages :as a]
-            [com.tailrecursion.blacktide.compiler.structured-languages :as s]
-            [com.tailrecursion.blacktide.compiler.graph :as graph]
-            [typed.clojure :as-alias t]))
-
-(declare edge body)
-
-(np/defpass block
-  {:from [l/Machine Label] :to [l/Machine Block] :args [l/Machine-Program] :body true}
-  [machine id]
-  (or (get (:blocks machine) id) (throw (IllegalStateException. "Missing checked block"))))
-
-(np/defpass scan
-  {:from [l/Machine Label] :to [s/Joined Scan]
-   :args [l/Machine-Program (t/Set l/Machine-Label) s/Joined-Scan] :body true}
-  [machine path state id]
-  (let [[seen headers] state]
-    (cond (contains? path id) [seen (conj headers id)]
-          (contains? seen id) state
-          :else (reduce (fn [^{::t/- s/Joined-Scan} state ^{::t/- l/Machine-Label} next]
-                          (scan machine (conj path id) state next))
-                        [(conj seen id) headers]
-                        (graph/successors (:term (block machine id)))))))
-
-(np/defpass harmless-atom? {:from [l/Machine Atom] :to t/Bool}
-  [atom]
-  :local true
-  :literal (nil? (:value atom)))
-
-(np/defpass harmless-block? {:from [l/Machine Block] :to t/Bool :body true}
-  [block]
-  ;; Lowering leaves nil result copies after transfers. No host/native form or
-  ;; arbitrary quoted constant may disappear before Clojure validates it.
-  (and (every? (fn [op] (let [rhs (:value op)]
-                         (and (= :value (:op rhs)) (harmless-atom? (:value rhs))))) (:ops block))
-       (let [term (:term block)]
-         (case (:op term)
-           :jump true
-           (:return :throw) (harmless-atom? (:value term))
-           (:branch :switch) (harmless-atom? (:test term))))))
-
-(np/defpass wrap
-  {:from [l/Machine Block] :to [s/Joined Expr] :args [s/Joined-Expr] :body true}
-  [end block]
-  (reduce (fn [^{::t/- s/Joined-Expr} body ^{::t/- l/Machine-Operation} op]
-            {:op :bind :binding op :body body}) end (reverse (:ops block))))
-
-(np/defpass arms
-  {:from [l/Machine Term] :to [s/Joined Arms] :args [s/Joined-Context t/Bool t/Int] :body true}
-  [ctx inside? budget term]
-  (if (= :switch (:op term))
-    ((fn ^{::t/- [t/Int t/Int (t/Vec s/Joined-Expr) (t/Vec l/Machine-Label) :-> s/Joined-Arms]} build [index budget out blocks]
-       (if (= index (count (:targets term))) {:op :built :budget budget :arms out :blocks blocks}
-         (let [result (edge ctx inside? budget (nth (:targets term) index))]
-           (if (= :built (:op result))
-             (recur (inc index) (:budget result) (conj out (:expr result)) (into blocks (:blocks result))) result)))) 0 budget [] [])
-    {:op :exhausted}))
-
-(np/defpass terminator
-  {:from [l/Machine Term] :to [s/Joined Build] :args [s/Joined-Context t/Bool t/Int]}
-  [ctx inside? budget term]
-  :jump (edge ctx inside? budget (:target term))
-  :return {:op :built :budget budget :blocks [] :expr term}
-  :throw {:op :built :budget budget :blocks [] :expr term}
-  :switch (let [result (arms ctx inside? budget term)]
-            (if (= :built (:op result))
-              {:op :built :budget (:budget result) :blocks (:blocks result) :expr {:op :switch :test (:test term) :arms (:arms result)}} result))
-  :branch (let [yes (edge ctx inside? budget (:yes term))]
-            (if (= :exhausted (:op yes)) yes
-              (let [no (edge ctx inside? (:budget yes) (:no term))]
-                (if (= :exhausted (:op no)) no
-                  {:op :built :budget (:budget no) :blocks (into (:blocks yes) (:blocks no))
-                   :expr {:op :branch :test (:test term) :yes (:expr yes) :no (:expr no)}})))))
-
-(np/defpass joined
-  {:from [a/Planned Join] :to [s/Joined Build] :args [s/Joined-Context t/Bool t/Int l/Machine-Term] :body true}
-  [ctx inside? budget term join]
-  (let [inner (assoc ctx :stop [(:target join) (:result join)])
-        result (terminator inner inside? budget term)]
-    (if (= :built (:op result))
-      (let [tail (edge ctx inside? (:budget result) (:target join))]
-        (if (= :built (:op tail))
-          (assoc tail :blocks (into (:blocks result) (:blocks tail))
-                      :expr {:op :join :target (:result join) :value (:expr result) :body (:expr tail)}) tail))
-      {:op :exhausted})))
-
-(np/defpass body
-  {:from [l/Machine Label] :to [s/Joined Build] :args [s/Joined-Context t/Bool t/Int] :body true}
-  [ctx inside? budget id]
-  (let [block (block (:machine ctx) id)
-        budget (- budget (inc (count (:ops block))))]
-    (if (neg? budget) {:op :exhausted}
-      (let [result (if-let [join (get (:joins ctx) id)]
-                     (joined ctx inside? budget (:term block) join)
-                     (terminator ctx inside? budget (:term block)))]
-        (if (= :built (:op result)) (assoc result :blocks (conj (:blocks result) id) :expr (wrap (:expr result) block)) result)))))
-
-(np/defpass edge
-  {:from [l/Machine Label] :to [s/Joined Build] :args [s/Joined-Context t/Bool t/Int] :body true}
-  [ctx inside? budget id]
-  (let [stop (:stop ctx)]
-    (cond
-      (and stop (= id (first stop)))
-      {:op :built :budget budget :blocks [] :expr {:op :return :value {:op :local :id (second stop)}}}
-      (= id (:header ctx))
-      (if inside? (if (pos? budget) {:op :built :budget (dec budget) :blocks [] :expr {:op :recur :slots (:slots ctx)}}
-                      {:op :exhausted})
-        (let [result (body ctx true (dec budget) id)]
-          (if (= :built (:op result)) (assoc result :expr {:op :loop :slots (:slots ctx) :body (:expr result)}) result)))
-      :else (body ctx inside? budget id))))
-
-(np/defpass construction-site?
-  {:from [l/Machine Rhs] :to t/Bool :body true}
-  [rhs]
-  (case (:op rhs)
-    :native true
-    :convert (let [conversion (:conversion rhs)]
-               (and (:parameter? conversion)
-                    (.isAnnotationPresent ^Class (:type conversion) java.lang.FunctionalInterface)))
-    :host (let [operation (:operation rhs)]
-            (case (:op operation)
-              (:keyword-site :method-value) true
-              :adapt (.isAnnotationPresent ^Class (:class operation) java.lang.FunctionalInterface)
-              :protocol (boolean (some (fn [^Class type] (.isAnnotationPresent type java.lang.FunctionalInterface))
-                                       (:parameters (:method operation))))
-              false))
-    false))
-
-(np/defpass site-failure
-  {:from [l/Machine Program] :to (t/Option ':native-sites)
-   :args [(t/Vec l/Machine-Label)] :body true}
-  [blocks machine]
-  (let [copies (frequencies blocks)
-        repeated (mapv (fn [[id _]] (block machine id)) (filter (fn [[_ n]] (> n 1)) copies))]
-    (when (some (fn [block]
-                  (some (fn [operation] (construction-site? (:value operation)))
-                        (:ops block))) repeated)
-      :native-sites)))
-
-(np/defpass recover {:from [a/Planned Program] :to [s/Joined Decision] :body true}
-  [analysis]
-  (let [machine (:machine analysis) reachable (:reachable analysis)
-        [_ headers] (scan machine #{} [#{} #{}] (:entry machine))
-        dead (apply dissoc (:blocks machine) reachable)
-        header (first headers)]
-    (cond
-      (> (count headers) 1) {:op :fallback :reason :cycles}
-      (not (every? harmless-block? (vals dead))) {:op :fallback :reason :validation}
-      :else
-      (let [slots (if header (vec (sort (get (:live machine) header #{}))) [])
-            ctx {:machine machine :header header :slots slots :joins (:joins analysis) :stop nil}
-            budget (* 4 (reduce + 0 (map #(inc (count (:ops %))) (vals (select-keys (:blocks machine) reachable)))))
-            result (edge ctx false budget (:entry machine))]
-        (if (= :built (:op result))
-          (if-let [reason (site-failure (:blocks result) machine)]
-            {:op :fallback :reason reason} {:op :structured :body (:expr result)})
-          {:op :fallback :reason :budget})))))
diff --git a/src/com/tailrecursion/blacktide/compiler/structured_emit.clj b/src/com/tailrecursion/blacktide/compiler/structured_emit.clj
deleted file mode 100644
index c0c1e5a..0000000
--- a/src/com/tailrecursion/blacktide/compiler/structured_emit.clj
+++ /dev/null
@@ -1,110 +0,0 @@
-(ns ^:typed.clojure com.tailrecursion.blacktide.compiler.structured-emit
-  "Emit lexical control with proven storage classes. Consumer source types stay
-  distinct from storage; Java argument conversions remain explicit evaluations."
-  (:require [com.tailrecursion.nanopass :as np]
-            [com.tailrecursion.blacktide.compiler.languages :as l]
-            [com.tailrecursion.blacktide.compiler.structured-languages :as s]
-            [com.tailrecursion.blacktide.compiler.compact-languages :as c]
-            [com.tailrecursion.blacktide.compiler.compact :as compact]
-            [com.tailrecursion.blacktide.compiler.collections :as collections]
-            [com.tailrecursion.blacktide.compiler.jvm :as jvm]
-            [typed.clojure :as-alias t]
-            [com.tailrecursion.blacktide.compiler.emit :as emit]
-            [com.tailrecursion.blacktide.compiler.host-emit :as host]
-            [com.tailrecursion.blacktide.compiler.case-emit :as cases]))
-
-(np/defpass primitive-form
-  {:from [l/Machine Code] :to [l/Machine Code] :args [java.lang.Class] :body true}
-  [^Class type form]
-  ;; The parameter conversion is exactly the conversion previously performed by
-  ;; RT.box(type); the matching primitive cast overload itself is an identity.
-  (list (with-meta (symbol "clojure.lang.RT" (str (.getName type) "Cast")) {:param-tags [type]}) form))
-
-(np/defpass binding-form
-  {:from [l/Machine Code] :to [l/Machine Code] :args [s/Stored-Types t/Sym (t/Option java.lang.Class)] :body true}
-  [types id actual form]
-  (if-let [type (get types id)] (if (= type actual) form (primitive-form type form)) (host/box-form form)))
-
-(declare rhs-form)
-(np/defpass atom-form {:from [c/Compact Atom] :to [l/Machine Code] :args [c/Compact-Types]}
-  [types node]
-  :local (:id node)
-  :literal (list 'quote (:value node))
-  :inline (rhs-form types (:value node)))
-
-(np/defpass operand-form
-  {:from [c/Compact Operand] :to [l/Machine Code] :args [c/Compact-Types] :body true}
-  [types operand]
-  (let [form (atom-form types (:value operand)) actual (compact/atom-type types (:value operand))
-        fact (:jvm operand)]
-    (if (and (class? actual) (= actual (:type fact))) form
-      (host/typed-form (assoc fact :primitive? false) (if (class? actual) (host/box-form form) form)))))
-
-(np/defpass rhs-form
-  {:from [c/Compact Rhs] :to [l/Machine Code] :args [c/Compact-Types]}
-  [types rhs]
-  :host (host/host-form (mapv #(operand-form types %) (:args rhs)) (:operation rhs))
-  :dispatch (cases/selector-form (operand-form types (:value rhs)) (:spec rhs))
-  :convert (let [conversion (:conversion rhs) operand (:value rhs)]
-             (if (and (:parameter? conversion) (.isPrimitive ^Class (:type conversion)))
-               (if (= (:type conversion) (compact/atom-type types (:value operand)) (:type (:jvm operand)))
-                 (atom-form types (:value operand))
-                 (primitive-form (:type conversion) (operand-form types operand)))
-               (host/conversion-form conversion (:jvm operand) (atom-form types (:value operand)))))
-  :value (atom-form types (:value rhs))
-  :global (emit/rhs-form rhs) :external (emit/rhs-form rhs) :var (emit/rhs-form rhs) :native (emit/rhs-form rhs)
-  :construct (collections/form (mapv #(atom-form types %) (:items rhs)) (:unique? rhs) (:kind rhs))
-  :metadata (let [value (gensym "value__") metadata (gensym "metadata__")]
-              (list 'let* [(with-meta value {:tag 'clojure.lang.IObj}) (atom-form types (:value rhs))
-                           (with-meta metadata {:tag 'clojure.lang.IPersistentMap}) (atom-form types (:metadata rhs))]
-                    (list '. value 'withMeta metadata)))
-  :invoke (list* (atom-form types (:function rhs)) (mapv #(atom-form types %) (:args rhs))))
-
-(np/defpass rhs-type
-  {:from [c/Compact Rhs] :to (t/Option java.lang.Class) :args [c/Compact-Types] :body true}
-  [types rhs]
-  (case (:op rhs)
-    :value (compact/atom-type types (:value rhs))
-    :convert (when (:parameter? (:conversion rhs)) (:type (:conversion rhs)))
-    :host (let [type (jvm/host-type (mapv #(:type (:jvm %)) (:args rhs)) (:operation rhs))]
-            (when (and (class? type) (not (#{:adapt} (:op (:operation rhs))))) type))
-    ;; Other producers retain an explicit storage conversion.
-    nil))
-
-(np/defpass operation-form
-  {:from [c/Compact Operation] :to [l/Machine Code] :args [c/Compact-Types] :body true}
-  [types op]
-  (binding-form types (:target op) (rhs-type types (:value op)) (rhs-form types (:value op))))
-
-(declare expression)
-(np/defpass let-form {:from [c/Compact Expr] :to [l/Machine Code] :args [c/Compact-Types] :body true}
-  [types node]
-  ((fn ^{::t/- [(t/Vec t/Any) c/Compact-Expr :-> l/Machine-Code]} bindings [pairs node]
-     (case (:op node)
-       :bind (let [op (:binding node)]
-               (recur (conj pairs (:target op) (operation-form types op)) (:body node)))
-       :join (recur (conj pairs (:target node)
-                          (binding-form types (:target node) nil (expression types (:value node)))) (:body node))
-       (list 'let* pairs (expression types node)))) [] node))
-
-(np/defpass expression {:from [c/Compact Expr] :to [l/Machine Code] :args [c/Compact-Types]}
-  [types node]
-  :bind (let-form types node)
-  :effect (let [binding (:binding node) rhs (:value binding) type (get types (:target binding))
-                form (rhs-form types rhs)]
-            (list 'do (if (and type (not= type (rhs-type types rhs))) (primitive-form type form) form)
-                  (expression types (:body node))))
-  :join (let-form types node)
-  :branch (list 'if (atom-form types (:test node)) (expression types (:yes node)) (expression types (:no node)))
-  :switch (cases/arms-form (mapv #(expression types %) (:arms node)) (atom-form types (:test node)))
-  :loop (list 'loop* (vec (mapcat (fn [binding]
-                                  [(:target binding) (binding-form types (:target binding)
-                                                       (compact/atom-type types (:value binding))
-                                                       (atom-form types (:value binding)))]) (:bindings node)))
-              (expression types (:body node)))
-  :recur (list* 'recur (mapv #(atom-form types %) (:args node)))
-  :return (let [value (:value node) form (atom-form types value)]
-            ;; Tail compaction must retain the region's opaque result type.
-            (if (and (= :inline (:op value)) (= :native (:op (:value value))))
-              (host/box-form form) form))
-  :throw (list 'throw (atom-form types (:value node))))
diff --git a/src/com/tailrecursion/blacktide/compiler/structured_languages.clj b/src/com/tailrecursion/blacktide/compiler/structured_languages.clj
deleted file mode 100644
index 9fbdf88..0000000
--- a/src/com/tailrecursion/blacktide/compiler/structured_languages.clj
+++ /dev/null
@@ -1,34 +0,0 @@
-(ns com.tailrecursion.blacktide.compiler.structured-languages
-  "Lexical control trees contain at most one loop target. General graph shapes remain
-  machines; bounded construction returns an explicit fallback, never partial code."
-  (:require [com.tailrecursion.nanopass :as np]
-            [com.tailrecursion.blacktide.compiler.languages :as l]
-            [com.tailrecursion.blacktide.compiler.analysis-languages :as a]
-            [typed.clojure :as-alias t]))
-
-(np/deflanguage Structured
-  {Expr (t/U (t/HMap :mandatory {:op ':bind :binding l/Machine-Operation :body Expr} :complete? true)
-             (t/HMap :mandatory {:op ':branch :test l/Machine-Atom :yes Expr :no Expr} :complete? true)
-             (t/HMap :mandatory {:op ':switch :test l/Machine-Atom :arms (t/Vec Expr)} :complete? true)
-             (t/HMap :mandatory {:op ':loop :slots (t/Vec t/Sym) :body Expr} :complete? true)
-             (t/HMap :mandatory {:op ':recur :slots (t/Vec t/Sym)} :complete? true)
-             (t/HMap :mandatory {:op ':return :value l/Machine-Atom} :complete? true)
-             (t/HMap :mandatory {:op ':throw :value l/Machine-Atom} :complete? true))
-   Build (t/U (t/HMap :mandatory {:op ':built :budget t/Int :blocks (t/Vec l/Machine-Label) :expr Expr} :complete? true)
-               (t/HMap :mandatory {:op ':exhausted} :complete? true))
-   Arms (t/U (t/HMap :mandatory {:op ':built :budget t/Int :blocks (t/Vec l/Machine-Label) :arms (t/Vec Expr)} :complete? true)
-              (t/HMap :mandatory {:op ':exhausted} :complete? true))
-   Scan (t/HVec [(t/Set l/Machine-Label) (t/Set l/Machine-Label)])
-   Fallback (t/U ':cycles ':validation ':budget ':native-sites)
-   Decision (t/U (t/HMap :mandatory {:op ':structured :body Expr} :complete? true)
-                 (t/HMap :mandatory {:op ':fallback :reason Fallback} :complete? true))})
-
-(np/derive-language Joined Structured
-  {:add {Expr [(t/HMap :mandatory {:op ':join :target t/Sym :value Expr :body Expr} :complete? true)]}
-   :replace {Context (t/HMap :mandatory {:machine l/Machine-Program :header (t/Option l/Machine-Label)
-                                        :slots (t/Vec t/Sym) :joins a/Planned-Joins
-                                        :stop (t/Option (t/HVec [l/Machine-Label t/Sym]))} :complete? true)}})
-
-(np/derive-language Stored Joined
-  {:replace {Types (t/Map t/Sym java.lang.Class)
-             Program (t/HMap :mandatory {:body Expr :types Types} :complete? true)}})
diff --git a/src/com/tailrecursion/nanopass.clj b/src/com/tailrecursion/nanopass.clj
index 58ff10c..1c609db 100644
--- a/src/com/tailrecursion/nanopass.clj
+++ b/src/com/tailrecursion/nanopass.clj
@@ -41,12 +41,8 @@
   [& languages]
   `(do ~@(language/type-forms (map description languages))))
 
-(defmacro defpass
-  "Define a checked pass. :from and :to identify [language nonterminal]; :to may
-  instead be a result type. :args types the leading context parameters. Supply
-  tag/expression clauses; unchanged productions get structural recursion through
-  :processors (nonterminal -> function). :body true accepts one explicit body."
-  [name {:keys [from to args processors body] :as options} arguments & clauses]
+(defn- pass-definition
+  [locals form name {:keys [from to args processors body] :as options} arguments clauses]
   (when-not (and (simple-symbol? name) (vector? from) (= 2 (count from))
                  (symbol? (first from)) (simple-symbol? (second from))
                  (contains? options :to)
@@ -60,19 +56,50 @@
                  (= (count arguments) (count (set arguments)))
                  (= (count arguments) (inc (count args)))
                  (every? #{:from :to :args :processors :body} (keys options)))
-    (language/fail :invalid-pass &form "Invalid pass name, signature, or options"))
+    (language/fail :invalid-pass form "Invalid pass name, signature, or options"))
   (let [source (description (first from))
         target (when (vector? to) (description (first to)))
         result-type (if target (language/type-name target (second to))
                         (language/qualify *ns* #{} to))]
     (with-meta (pass/definition
-     {:name (symbol (str (ns-name *ns*)) (str name))
+     {:name (if (locals name) name (symbol (str (ns-name *ns*)) (str name)))
       :source source :target target :from (second from) :to (when target (second to))
       :params (mapv #(language/qualify *ns* #{} %) args) :arguments arguments
       :result-type result-type
-      :processors (into {} (map (fn [[nt processor]] [nt (language/qualify *ns* #{} processor)])) processors)
+      :processors (into {} (map (fn [[nt processor]] [nt (if (locals processor) processor (language/qualify *ns* #{} processor))])) processors)
       :body? body}
-     clauses) (meta &form))))
+     clauses) (meta form))))
+
+(defmacro defpass
+  "Define a checked pass. :from and :to identify [language nonterminal]; :to may
+  instead be a result type. :args types the leading context parameters. Supply
+  tag/expression clauses; unchanged productions get structural recursion through
+  :processors (nonterminal -> function). :body true accepts one explicit body."
+  [name options arguments & clauses]
+  (pass-definition #{} &form name options arguments clauses))
+
+(defmacro defpasses
+  "Define mutually recursive processors as one lexical letfn group. Each entry
+  has the defpass syntax without its macro name. All functions retain their
+  individual checker signatures; recursion uses locals, not forward Vars."
+  [& definitions]
+  (let [names (map first definitions) locals (set names)]
+    (when-not (and (seq definitions) (every? simple-symbol? names)
+                   (= (count names) (count locals)))
+      (language/fail :invalid-pass &form "Expected distinct processor names"))
+    ;; Typed Clojure 1.3 collects letfn bindings in a map before matching its
+    ;; positional annotations. Emit that same order, including groups over eight.
+    (let [functions (vals (into {} (map (fn [[name options arguments & clauses :as form]]
+                                         [name (pass-definition locals form name options arguments clauses)])) definitions))
+          signatures (mapv #(second (::signature (nth % 2))) functions)]
+      `(letfn [~@(map (fn [[_ name _ args body :as form]]
+                       (with-meta (list name args body) (meta form))) functions)]
+         ;; Typed Clojure's letfn annotation is an inert quoted vector, in
+         ;; binding order. It imposes no checker dependency at runtime.
+         '~signatures
+         ~@(map (fn [[_ name attributes args :as form]]
+                   `(def ~(with-meta name (assoc (merge (meta form) attributes)
+                                                :arglists (list 'quote (list args)))) ~name)) functions)))))
 
 (defmacro ann-passes
   "Emit checker signatures stored by defpass. The generated function bodies
diff --git a/src/com/tailrecursion/nanopass/pass.clj b/src/com/tailrecursion/nanopass/pass.clj
index c09ce38..fe84a5b 100644
--- a/src/com/tailrecursion/nanopass/pass.clj
+++ b/src/com/tailrecursion/nanopass/pass.clj
@@ -11,8 +11,6 @@
     `((fn [~(typed bad 'typed.clojure/Nothing)] (throw (ex-info "Unexpected nanopass production"
                                 {:pass '~pass :node ~bad}))) ~value)))
 
-(declare walk dispatch)
-
 (defn- preserve-meta [{:keys [target]} type value result]
   (let [type (l/instantiate target type)
         id (gensym "result__")
@@ -23,118 +21,117 @@
          (~attach ~id (meta ~value))
          ~id))))
 
-(defn- mapper [{:keys [source] :as context} in out]
-  (let [item (gensym "item__")]
-    `(fn [~(typed item (l/instantiate source in))] ~(walk context in out item))))
-
-(defn- map-fields [{:keys [source target] :as context} in out value]
-  (let [a (l/hmap-options in) b (l/hmap-options out)]
-    (when-not (every? #(= (set (keys (% a))) (set (keys (% b)))) [:mandatory :optional])
-      (l/fail :explicit-clause-required in "Changed map fields need an explicit clause"))
-    (let [changed (filter (fn [[key type]]
-                            (or (not= type (get-in b [:optional key]))
-                                (seq (l/references (set (keys (:types source))) type))))
-                          (sort-by (comp str key) (:optional a)))
-          base (if (seq changed) `(dissoc ~value ~@(map first changed)) value)
-          fields (mapcat (fn [[key type]]
-                           (let [before `(~key ~value)
-                                 after (walk context type (get-in b [:mandatory key]) before)]
-                             (when (not= before after) [key after]))) (:mandatory a))
-          required (if (seq fields) `(assoc ~base ~@fields) base)]
-      ;; assoc/dissoc preserve metadata. Retain unchanged fields and reconstruct
-      ;; only recursive or changed fields; no metadata wrapper is needed.
-      (reduce (fn [result [key type]]
-                (let [acc (gensym "record__") child (gensym "child__")
-                      other-keys (remove #{key} (concat (keys (:mandatory a)) (keys (:optional a))))]
-                  ;; Project a zero/one-entry map. Its value type excludes
-                  ;; missing-key nil while retaining an explicitly present nil.
-                  `(reduce-kv
-                    (fn [~(typed acc (l/instantiate target out)) ~'_ ~(typed child (l/instantiate source type))]
-                      (assoc ~acc ~key ~(walk context type (get-in b [:optional key]) child)))
-                    ~result (dissoc ~value ~@other-keys))))
-              required changed))))
+(defn definition
+  [{:keys [name source target from to params arguments result-type processors body?]} forms]
+  (letfn [(mapper [{:keys [source] :as context} in out]
+            (let [item (gensym "item__")]
+              `(fn [~(typed item (l/instantiate source in))] ~(walk context in out item))))
 
-(defn- optional [context in out value]
-  (let [id (gensym "optional__")]
-    `((fn [~(typed id (list 'typed.clojure/Option (l/instantiate (:source context) in)))]
-        (if (nil? ~id) nil ~(walk context in out id))) ~value)))
+          (map-fields [{:keys [source target] :as context} in out value]
+            (let [a (l/hmap-options in) b (l/hmap-options out)]
+              (when-not (every? #(= (set (keys (% a))) (set (keys (% b)))) [:mandatory :optional])
+                (l/fail :explicit-clause-required in "Changed map fields need an explicit clause"))
+              (let [changed (filter (fn [[key type]]
+                                      (or (not= type (get-in b [:optional key]))
+                                          (seq (l/references (set (keys (:types source))) type))))
+                                    (sort-by (comp str key) (:optional a)))
+                    base (if (seq changed) `(dissoc ~value ~@(map first changed)) value)
+                    fields (mapcat (fn [[key type]]
+                                     (let [before `(~key ~value)
+                                           after (walk context type (get-in b [:mandatory key]) before)]
+                                       (when (not= before after) [key after]))) (:mandatory a))
+                    required (if (seq fields) `(assoc ~base ~@fields) base)]
+                ;; assoc/dissoc preserve metadata. Retain unchanged fields and reconstruct
+                ;; only recursive or changed fields; no metadata wrapper is needed.
+                (reduce (fn [result [key type]]
+                          (let [acc (gensym "record__") child (gensym "child__")
+                                other-keys (remove #{key} (concat (keys (:mandatory a)) (keys (:optional a))))]
+                            ;; Project a zero/one-entry map. Its value type excludes
+                            ;; missing-key nil while retaining an explicitly present nil.
+                            `(reduce-kv
+                              (fn [~(typed acc (l/instantiate target out)) ~'_ ~(typed child (l/instantiate source type))]
+                                (assoc ~acc ~key ~(walk context type (get-in b [:optional key]) child)))
+                              ~result (dissoc ~value ~@other-keys))))
+                        required changed))))
 
-(defn- walk [{:keys [source target processors params] :as context} in out value]
-  (cond
-    (and (symbol? in) (contains? (:types source) in))
-    (if-let [processor (and (contains? (:types target) out) (get processors in))]
-      `(~processor ~@params ~value)
-      (l/fail :missing-processor in "Recursive field needs a processor" {:target out}))
+          (optional [context in out value]
+            (let [id (gensym "optional__")]
+              `((fn [~(typed id (list 'typed.clojure/Option (l/instantiate (:source context) in)))]
+                  (if (nil? ~id) nil ~(walk context in out id))) ~value)))
 
-    (and (= in out) (empty? (l/references (set (keys (:types source))) in))) value
+          (walk [{:keys [source target processors params] :as context} in out value]
+            (cond
+              (and (symbol? in) (contains? (:types source) in))
+              (if-let [processor (and (contains? (:types target) out) (get processors in))]
+                `(~processor ~@params ~value)
+                (l/fail :missing-processor in "Recursive field needs a processor" {:target out}))
 
-    (not (and (seq? in) (seq? out) (= (first in) (first out))))
-    (l/fail :explicit-clause-required in "Changed field types need an explicit clause" {:target out})
+              (and (= in out) (empty? (l/references (set (keys (:types source))) in))) value
 
-    :else
-    (case (first in)
-      typed.clojure/HMap (map-fields context in out value)
-      typed.clojure/Vec (preserve-meta context out value `(mapv ~(mapper context (second in) (second out)) ~value))
-      typed.clojure/Set (preserve-meta context out value `(into #{} (map ~(mapper context (second in) (second out))) ~value))
-      typed.clojure/HVec
-      (do (when-not (= (count (second in)) (count (second out)))
-            (l/fail :explicit-clause-required in "Changed tuple arity needs an explicit clause"))
-          (preserve-meta context out value
-                         (mapv (fn [i a b] (walk context a b `(nth ~value ~i)))
-                               (range) (second in) (second out))))
-      typed.clojure/Map
-      (let [[_ ik iv] in [_ ok ov] out
-            acc (gensym "map__") k (gensym "key__") v (gensym "val__")]
-        (preserve-meta context out value
-                       `(reduce-kv (fn [~(typed acc (l/instantiate target out))
-                                        ~(typed k (l/instantiate source ik))
-                                        ~(typed v (l/instantiate source iv))]
-                                     (assoc ~acc ~(walk context ik ok k) ~(walk context iv ov v))) {} ~value)))
-      typed.clojure/Option (optional context (second in) (second out) value)
-      typed.clojure/U
-      (let [a (l/alternatives in) b (l/alternatives out)]
-        (if (and (= 2 (count a) (count b)) (some nil? a) (some nil? b))
-          (optional context (first (remove nil? a)) (first (remove nil? b)) value)
-          (dispatch context in out value {})))
-      (l/fail :explicit-clause-required in "Cannot derive structural traversal for this type"))))
+              (not (and (seq? in) (seq? out) (= (first in) (first out))))
+              (l/fail :explicit-clause-required in "Changed field types need an explicit clause" {:target out})
 
-(defn- dispatch [{:keys [name] :as context} in out value clauses]
-  (let [variants (l/alternatives in)
-        tags (set (map l/tag variants))
-        targets (when out (into {} (map (juxt l/tag identity)) (l/alternatives out)))]
-    (when (contains? tags nil)
-      (l/fail :ambiguous-dispatch in "Dispatch needs tagged map alternatives; use :body true"))
-    (when-not (every? tags (keys clauses))
-      (l/fail :unknown-production clauses "Pass clause is absent from the input language"))
-    `(case (:op ~value)
-       ~@(mapcat (fn [variant]
-                   (let [tag (l/tag variant)]
-                     [tag (if (contains? clauses tag) (get clauses tag)
-                            (if-let [other (get targets tag)]
-                              (walk context variant other value)
-                              (l/fail :missing-clause variant "Removed production needs a clause"
-                                      {:pass name :production tag})))])) variants)
-       ~(unexpected name value))))
+              :else
+              (case (first in)
+                typed.clojure/HMap (map-fields context in out value)
+                typed.clojure/Vec (preserve-meta context out value `(mapv ~(mapper context (second in) (second out)) ~value))
+                typed.clojure/Set (preserve-meta context out value `(into #{} (map ~(mapper context (second in) (second out))) ~value))
+                typed.clojure/HVec
+                (do (when-not (= (count (second in)) (count (second out)))
+                      (l/fail :explicit-clause-required in "Changed tuple arity needs an explicit clause"))
+                    (preserve-meta context out value
+                                   (mapv (fn [i a b] (walk context a b `(nth ~value ~i)))
+                                         (range) (second in) (second out))))
+                typed.clojure/Map
+                (let [[_ ik iv] in [_ ok ov] out
+                      acc (gensym "map__") k (gensym "key__") v (gensym "val__")]
+                  (preserve-meta context out value
+                                 `(reduce-kv (fn [~(typed acc (l/instantiate target out))
+                                                  ~(typed k (l/instantiate source ik))
+                                                  ~(typed v (l/instantiate source iv))]
+                                               (assoc ~acc ~(walk context ik ok k) ~(walk context iv ov v))) {} ~value)))
+                typed.clojure/Option (optional context (second in) (second out) value)
+                typed.clojure/U
+                (let [a (l/alternatives in) b (l/alternatives out)]
+                  (if (and (= 2 (count a) (count b)) (some nil? a) (some nil? b))
+                    (optional context (first (remove nil? a)) (first (remove nil? b)) value)
+                    (dispatch context in out value {})))
+                (l/fail :explicit-clause-required in "Cannot derive structural traversal for this type"))))
 
-(defn definition
-  [{:keys [name source target from to params arguments result-type processors body?]} forms]
-  (let [node (last arguments)
-        input-type (l/type-name source from)
-        signature (vec (concat params [input-type :-> result-type]))
-        context {:name name :source source :target target
-                 :processors (merge {from name} processors) :params (vec (butlast arguments))}
-        in (get-in source [:types from]) out (get-in target [:types to])
-        body (if body?
-               (do (when-not (= 1 (count forms))
-                     (l/fail :invalid-pass forms "A whole-body pass needs one expression"))
-                   (first forms))
-               (do (when (or (odd? (count forms))
-                             (not= (count (take-nth 2 forms)) (count (set (take-nth 2 forms)))))
-                     (l/fail :invalid-pass forms "Pass clauses must be distinct tag/expression pairs"))
-                   (if (and (empty? forms) (not-every? l/tag (l/alternatives in)))
-                     (walk context in out node)
-                     (dispatch context in out node (apply hash-map forms)))))
-        local-name (symbol (clojure.core/name name))]
-    `(defn ~local-name
-       {:com.tailrecursion.nanopass/signature '~signature}
-       ~arguments ~body)))
+          (dispatch [{:keys [name] :as context} in out value clauses]
+            (let [variants (l/alternatives in)
+                  tags (set (map l/tag variants))
+                  targets (when out (into {} (map (juxt l/tag identity)) (l/alternatives out)))]
+              (when (contains? tags nil)
+                (l/fail :ambiguous-dispatch in "Dispatch needs tagged map alternatives; use :body true"))
+              (when-not (every? tags (keys clauses))
+                (l/fail :unknown-production clauses "Pass clause is absent from the input language"))
+              `(case (:op ~value)
+                 ~@(mapcat (fn [variant]
+                             (let [tag (l/tag variant)]
+                               [tag (if (contains? clauses tag) (get clauses tag)
+                                      (if-let [other (get targets tag)]
+                                        (walk context variant other value)
+                                        (l/fail :missing-clause variant "Removed production needs a clause"
+                                                {:pass name :production tag})))])) variants)
+                 ~(unexpected name value))))]
+    (let [node (last arguments)
+          input-type (l/type-name source from)
+          signature (vec (concat params [input-type :-> result-type]))
+          context {:name name :source source :target target
+                   :processors (merge {from name} processors) :params (vec (butlast arguments))}
+          in (get-in source [:types from]) out (get-in target [:types to])
+          body (if body?
+                 (do (when-not (= 1 (count forms))
+                       (l/fail :invalid-pass forms "A whole-body pass needs one expression"))
+                     (first forms))
+                 (do (when (or (odd? (count forms))
+                               (not= (count (take-nth 2 forms)) (count (set (take-nth 2 forms)))))
+                       (l/fail :invalid-pass forms "Pass clauses must be distinct tag/expression pairs"))
+                     (if (and (empty? forms) (not-every? l/tag (l/alternatives in)))
+                       (walk context in out node)
+                       (dispatch context in out node (apply hash-map forms)))))
+          local-name (symbol (clojure.core/name name))]
+      `(defn ~local-name
+         {:com.tailrecursion.nanopass/signature '~signature}
+         ~arguments ~body))))
diff --git a/test/com/tailrecursion/blacktide/annotations.clj b/test/com/tailrecursion/blacktide/annotations.clj
index 6166da3..9810341 100644
--- a/test/com/tailrecursion/blacktide/annotations.clj
+++ b/test/com/tailrecursion/blacktide/annotations.clj
@@ -1,47 +1,30 @@
 (ns com.tailrecursion.blacktide.annotations
-  "Checking-only aliases and signatures for every production compiler pass."
-  (:require [typed.clojure]
+  "Discover compiler namespaces and register their language and pass contracts.
+  New source files participate in checking without a second handwritten list."
+  (:require [clojure.java.io :as io]
+            [typed.clojure]
             [clojure.core.typed :as ct]
-            [com.tailrecursion.nanopass :as np]
-            [com.tailrecursion.blacktide.compiler.assignment :as assignment]
-            [com.tailrecursion.blacktide.compiler.backend :as backend]
-            [com.tailrecursion.blacktide.compiler.boundaries :as boundaries]
-            [com.tailrecursion.blacktide.compiler.box-origins :as box-origins]
-            [com.tailrecursion.blacktide.compiler.case-emit :as case-emit]
-            [com.tailrecursion.blacktide.compiler.cases :as cases]
-            [com.tailrecursion.blacktide.compiler.collections :as collections]
-            [com.tailrecursion.blacktide.compiler.compact :as compact]
-            [com.tailrecursion.blacktide.compiler.compact-languages :as compact-languages]
-            [com.tailrecursion.blacktide.compiler.effects :as effects]
-            [com.tailrecursion.blacktide.compiler.emit :as emit]
-            [com.tailrecursion.blacktide.compiler.errors :as errors]
-            [com.tailrecursion.blacktide.compiler.feasible :as feasible]
-            [com.tailrecursion.blacktide.compiler.graph :as graph]
-            [com.tailrecursion.blacktide.compiler.host-emit :as host-emit]
-            [com.tailrecursion.blacktide.compiler.invariants :as invariants]
-            [com.tailrecursion.blacktide.compiler.joins :as joins]
-            [com.tailrecursion.blacktide.compiler.jvm :as jvm]
-            [com.tailrecursion.blacktide.compiler.jvm-languages :as jvm-languages]
-            [com.tailrecursion.blacktide.compiler.languages :as languages]
-            [com.tailrecursion.blacktide.compiler.loop-facts :as loop-facts]
-            [com.tailrecursion.blacktide.compiler.lower :as lower]
-            [com.tailrecursion.blacktide.compiler.native :as native]
-            [com.tailrecursion.blacktide.compiler.analysis-languages :as analysis-languages]
-            [com.tailrecursion.blacktide.compiler.order :as order]
-            [com.tailrecursion.blacktide.compiler.prepare :as prepare]
-            [com.tailrecursion.blacktide.compiler.source :as source]
-            [com.tailrecursion.blacktide.compiler.source-case :as source-case]
-            [com.tailrecursion.blacktide.compiler.source-host :as source-host]
-            [com.tailrecursion.blacktide.compiler.source-jvm :as source-jvm]
-            [com.tailrecursion.blacktide.compiler.source-languages :as source-languages]
-            [com.tailrecursion.blacktide.compiler.source-values :as source-values]
-            [com.tailrecursion.blacktide.compiler.storage :as storage]
-            [com.tailrecursion.blacktide.compiler.structure :as structure]
-            [com.tailrecursion.blacktide.compiler.structured-emit :as structured-emit]
-            [com.tailrecursion.blacktide.compiler.structured-languages :as structured-languages]
-            [com.tailrecursion.blacktide.compiler.transfers :as transfers]
-            [com.tailrecursion.blacktide.compiler.value-facts :as value-facts]
-            [com.tailrecursion.blacktide.compiler :as compiler]))
+            [com.tailrecursion.nanopass :as np]))
+
+(def compiler-namespaces
+  (->> (cons (io/file "src/com/tailrecursion/blacktide/compiler.clj")
+             (file-seq (io/file "src/com/tailrecursion/blacktide/compiler")))
+       (filter #(.endsWith (.getName ^java.io.File %) ".clj"))
+       (map (fn [file]
+              (with-open [reader (java.io.PushbackReader. (io/reader file))]
+                (binding [*read-eval* false] (second (read reader))))))
+       sort vec))
+
+(doseq [namespace compiler-namespaces] (require namespace))
+
+(defmacro annotate-compiler []
+  (let [vars (mapcat (comp vals ns-interns) compiler-namespaces)
+        names (fn [key]
+                (->> vars (filter #(key (meta %))) (map #(symbol %)) sort))]
+    `(do (np/ann-languages ~@(names ::np/language))
+         (np/ann-passes ~@(names ::np/signature)))))
+
+(annotate-compiler)
 
 ;; Var.toSymbol delegates to Symbol.create; its successful result is non-null.
 (ct/non-nil-return clojure.lang.Var/toSymbol :all)
@@ -57,82 +40,3 @@
 (ct/non-nil-return java.lang.reflect.Method/getReturnType :all)
 (ct/non-nil-return java.lang.reflect.Method/getDeclaringClass :all)
 (ct/non-nil-return java.lang.reflect.Method/getParameterTypes :all)
-
-(np/ann-languages compact-languages/Compact)
-(np/ann-languages jvm-languages/JVM)
-(np/ann-languages languages/Machine languages/CFG languages/Symbolic)
-(np/ann-languages analysis-languages/Analysis analysis-languages/Planned)
-(np/ann-languages source-languages/Bound source-languages/Native source-languages/Classified
-               source-languages/Sequential source-languages/Assigned source-languages/Cases
-               source-languages/Collections source-languages/Prepared source-languages/Ordered)
-(np/ann-languages structured-languages/Structured structured-languages/Joined structured-languages/Stored)
-(np/ann-passes assignment/sequential-pair assignment/sequential-pairs assignment/sequential-forms
-               assignment/sequential-node assignment/sequential-binding assignment/sequential
-               assignment/parallel-forms assignment/parallel-bindings assignment/save-parallel-values
-               assignment/parallel-node assignment/parallel)
-(np/ann-passes backend/compile-program)
-(np/ann-passes boundaries/native-node boundaries/native-binding boundaries/native-forms boundaries/native
-               boundaries/classify-node boundaries/classify-pair boundaries/classify-pairs
-               boundaries/classify-binding boundaries/classify-forms boundaries/classify)
-(np/ann-passes box-origins/copy-source box-origins/reboxable? box-origins/reboxable-values)
-(np/ann-passes case-emit/selector-form case-emit/arms-form)
-(np/ann-passes cases/normalize-node cases/normalize-binding cases/normalize-forms cases/normalize)
-(np/ann-passes collections/form collections/normalize-forms collections/literal-values collections/constant
-               collections/normalize-node collections/normalize-binding collections/normalize)
-(np/ann-passes compact/introduce compact/atom-reads compact/rhs-reads compact/reads compact/stable?
-               compact/substitute-atom compact/substitute-operand compact/substitute-rhs
-               compact/substitute-operation compact/substitute-binding compact/substitute compact/atom-type
-               compact/movable? compact/replacement compact/discardable? compact/simplify compact/program)
-(np/ann-passes effects/effect-node effects/effects)
-(np/ann-passes emit/atom-form emit/rhs-form emit/operation-form emit/edge-form emit/term-form emit/block-form
-               emit/program-form)
-(np/ann-passes errors/site errors/fail errors/check-visibility)
-(np/ann-passes feasible/terminator feasible/block feasible/program)
-(np/ann-passes graph/atom-reads graph/rhs-reads graph/term-reads graph/successors graph/block-facts
-               graph/collect-facts graph/fact-at graph/validate-edges graph/reachable graph/predecessors
-               graph/initialized graph/validate-initialization graph/liveness graph/allocate graph/analyze)
-(np/ann-passes host-emit/global-form host-emit/box-form host-emit/typed-form host-emit/conversion-form
-               host-emit/host-form)
-(np/ann-passes invariants/assigned invariants/expression)
-(np/ann-passes joins/postdominators joins/region joins/candidate joins/plan)
-(np/ann-passes jvm/resolved-class jvm/source-tag jvm/tag-class jvm/fact jvm/value-type jvm/hint-conflict
-               jvm/visible-class jvm/literal-type jvm/join-type jvm/methods jvm/static-method? jvm/return-type
-               jvm/constructors jvm/parameters jvm/select-method jvm/public-member jvm/host-type
-               jvm/prepare-call)
-(np/ann-passes loop-facts/children loop-facts/primitive loop-facts/inferred loop-facts/converted
-               loop-facts/boxed loop-facts/initializer loop-facts/bindings loop-facts/expression
-               loop-facts/recurs loop-facts/compatible? loop-facts/solve loop-facts/recur-error
-               loop-facts/recurrence loop-facts/materialize loop-facts/lower)
-(np/ann-passes lower/fresh-block lower/start lower/seal lower/evaluate lower/lower-forms lower/lower-bindings
-               lower/lower-node lower/lower lower/compile-graph)
-(np/ann-passes native/hinted-form native/reads-node native/boxed-reads native/reads native/literal-values
-               native/bindings-form native/node-form native/form)
-(np/ann-passes order/save order/operands order/normalize-forms order/normalize-bindings order/normalize-node
-               order/normalize)
-(np/ann-passes prepare/prepare-forms prepare/conversion prepare/prepare-node prepare/prepare-binding
-               prepare/prepare)
-(np/ann-passes source/fresh source/expression source/resolve-name source/parse-symbol source/parse-operand
-               source/parse-forms source/parse-body source/bind-local source/parse-bindings source/parse-scope
-               source/parse-loop source/parse-recur source/parse-arms source/case-expression source/parse-case
-               source/parse-dispatch source/protocol-argument source/protocol-call source/keyword-call
-               source/parse-call source/parse-host source/parse-assignment source/parse-marker
-               source/parse-list source/parse-collection source/parse)
-(np/ann-passes source-case/expand-case source-case/int32 source-case/dispatch-syntax)
-(np/ann-passes source-host/tags source-host/qualified source-host/syntax)
-(np/ann-passes source-jvm/signature source-jvm/primitive-interface source-jvm/direct-call
-               source-jvm/protocol-class source-jvm/protocol-call source-jvm/infer source-jvm/binding-facts
-               source-jvm/caller-facts)
-(np/ann-passes source-values/node source-values/binding-form source-values/pair source-values/pairs
-               source-values/forms source-values/expression)
-(np/ann-passes storage/loop-slots storage/cyclic? storage/operand-boxes storage/operation-boxes
-               storage/observed-values storage/term-boxes storage/select-storage)
-(np/ann-passes structure/block structure/scan structure/harmless-atom? structure/harmless-block?
-               structure/wrap structure/arms structure/terminator structure/joined structure/body
-               structure/edge structure/construction-site? structure/site-failure structure/recover)
-(np/ann-passes structured-emit/primitive-form structured-emit/binding-form structured-emit/atom-form
-               structured-emit/operand-form structured-emit/rhs-form structured-emit/rhs-type
-               structured-emit/operation-form structured-emit/let-form structured-emit/expression)
-(np/ann-passes transfers/resolve-term transfers/resolve-block transfers/resolve-program)
-(np/ann-passes value-facts/primitive value-facts/join value-facts/atom-type value-facts/rhs-type
-               value-facts/definitions value-facts/solve value-facts/analyze)
-(np/ann-passes compiler/compile-tree compiler/caller-locals compiler/compile-form)
diff --git a/test/com/tailrecursion/blacktide/backend_test.clj b/test/com/tailrecursion/blacktide/backend_test.clj
index b24281b..19e0309 100644
--- a/test/com/tailrecursion/blacktide/backend_test.clj
+++ b/test/com/tailrecursion/blacktide/backend_test.clj
@@ -5,24 +5,24 @@
             [clojure.test.check.generators :as gen]
             [clojure.test.check.properties :as prop]
             [com.tailrecursion.blacktide :refer [loophole label goto setq psetq]]
-            [com.tailrecursion.blacktide.compiler.backend :as backend]
-            [com.tailrecursion.blacktide.compiler.emit :as emit]
-            [com.tailrecursion.blacktide.compiler.structure :as structure]
-            [com.tailrecursion.blacktide.compiler.storage :as storage]
-            [com.tailrecursion.blacktide.compiler.compact :as compact]
-            [com.tailrecursion.blacktide.compiler.value-facts :as facts]
-            [com.tailrecursion.blacktide.compiler.box-origins :as origins]
-            [com.tailrecursion.blacktide.compiler.graph :as graph]
+            [com.tailrecursion.blacktide.compiler :as compiler]
+            [com.tailrecursion.blacktide.compiler.passes.emit :as emit]
+            [com.tailrecursion.blacktide.compiler.passes.structure :as structure]
+            [com.tailrecursion.blacktide.compiler.passes.storage :as storage]
+            [com.tailrecursion.blacktide.compiler.passes.compact :as compact]
+            [com.tailrecursion.blacktide.compiler.passes.value-facts :as facts]
+            [com.tailrecursion.blacktide.compiler.passes.box-origins :as origins]
+            [com.tailrecursion.blacktide.compiler.passes.graph :as graph]
             [com.tailrecursion.blacktide.compiler.jvm :as jvm]
-            [com.tailrecursion.blacktide.compiler.prepare :as prepare]
-            [com.tailrecursion.blacktide.compiler.lower :as lower]
+            [com.tailrecursion.blacktide.compiler.passes.prepare :as prepare]
+            [com.tailrecursion.blacktide.compiler.passes.lower :as lower]
             [com.tailrecursion.blacktide.machine-test :as m]))
 
 (defn compile-program [form mode]
   (binding [*ns* (the-ns 'com.tailrecursion.blacktide.backend-test)]
-    (with-redefs [backend/compile-program (if (= mode :dispatcher)
+    (with-redefs [compiler/compile-program (if (= mode :dispatcher)
                                                   emit/program-form
-                                                  backend/compile-program)]
+                                                  compiler/compile-program)]
       (eval (list 'fn '[events] form)))))
 
 (defn selection [form]
diff --git a/test/com/tailrecursion/blacktide/check.clj b/test/com/tailrecursion/blacktide/check.clj
index 50f3b29..7779b34 100644
--- a/test/com/tailrecursion/blacktide/check.clj
+++ b/test/com/tailrecursion/blacktide/check.clj
@@ -3,75 +3,45 @@
   (:require [clojure.test :refer [deftest is]]
             [typed.clojure :as t]
             [com.tailrecursion.nanopass.check :as probes]
-            [com.tailrecursion.blacktide.annotations]))
+            [com.tailrecursion.blacktide.annotations :as annotations]))
 
 (deftest compiler-passes
-  (is (t/check-ns-clj 'com.tailrecursion.blacktide.compiler.errors))
-  (is (t/check-ns-clj 'com.tailrecursion.blacktide.compiler.feasible))
-  (is (t/check-ns-clj 'com.tailrecursion.blacktide.compiler.value-facts))
-  (is (t/check-ns-clj 'com.tailrecursion.blacktide.compiler.box-origins))
-  (is (t/check-ns-clj 'com.tailrecursion.blacktide.compiler.invariants))
-  (is (t/check-ns-clj 'com.tailrecursion.blacktide.compiler.structure))
-  (is (t/check-ns-clj 'com.tailrecursion.blacktide.compiler.joins))
-  (is (t/check-ns-clj 'com.tailrecursion.blacktide.compiler.storage))
-  (is (t/check-ns-clj 'com.tailrecursion.blacktide.compiler.compact))
-  (is (t/check-ns-clj 'com.tailrecursion.blacktide.compiler.structured-emit))
-  (is (t/check-ns-clj 'com.tailrecursion.blacktide.compiler.backend))
-  (is (t/check-ns-clj 'com.tailrecursion.blacktide.compiler.jvm))
-  (is (t/check-ns-clj 'com.tailrecursion.blacktide.compiler.source-host))
-  (is (t/check-ns-clj 'com.tailrecursion.blacktide.compiler.source-jvm))
-  (is (t/check-ns-clj 'com.tailrecursion.blacktide.compiler.source-case))
-  (is (t/check-ns-clj 'com.tailrecursion.blacktide.compiler.cases))
-  (is (t/check-ns-clj 'com.tailrecursion.blacktide.compiler.case-emit))
-  (is (t/check-ns-clj 'com.tailrecursion.blacktide.compiler.host-emit))
-  (is (t/check-ns-clj 'com.tailrecursion.blacktide.compiler.emit))
-  (is (t/check-ns-clj 'com.tailrecursion.blacktide.compiler.graph))
-  (is (t/check-ns-clj 'com.tailrecursion.blacktide.compiler.source))
-  (is (t/check-ns-clj 'com.tailrecursion.blacktide.compiler.loop-facts))
-  (is (t/check-ns-clj 'com.tailrecursion.blacktide.compiler.effects))
-  (is (t/check-ns-clj 'com.tailrecursion.blacktide.compiler.boundaries))
-  (is (t/check-ns-clj 'com.tailrecursion.blacktide.compiler.native))
-  (is (t/check-ns-clj 'com.tailrecursion.blacktide.compiler.assignment))
-  (is (t/check-ns-clj 'com.tailrecursion.blacktide.compiler.source-values))
-  (is (t/check-ns-clj 'com.tailrecursion.blacktide.compiler.collections))
-  (is (t/check-ns-clj 'com.tailrecursion.blacktide.compiler.prepare))
-  (is (t/check-ns-clj 'com.tailrecursion.blacktide.compiler.order))
-  (is (t/check-ns-clj 'com.tailrecursion.blacktide.compiler.lower))
-  (is (t/check-ns-clj 'com.tailrecursion.blacktide.compiler.transfers))
-  (is (t/check-ns-clj 'com.tailrecursion.blacktide.compiler)))
+  (doseq [namespace annotations/compiler-namespaces
+          :when (not= namespace 'com.tailrecursion.blacktide.compiler.languages)]
+    (is (t/check-ns-clj namespace) (str namespace))))
 
 (deftest assignment-stage-exclusions
   (let [expr (fn [node] {:site {:form nil :file nil :line nil :column nil}
                          :jvm {:hint nil :var-meta {} :type :unknown :primitive? false :hint-error nil}
                          :node node})
         batch {:op :psetq :pairs []}
-        assigned 'com.tailrecursion.blacktide.compiler.source-languages/Assigned-Expr]
-    (probes/accepts? 'com.tailrecursion.blacktide.compiler.source-languages/Bound-Node {:op :setq :pairs []} true)
-    (probes/accepts? 'com.tailrecursion.blacktide.compiler.source-languages/Sequential-Node {:op :setq :pairs []} false)
-    (probes/accepts? 'com.tailrecursion.blacktide.compiler.source-languages/Sequential-Node batch true)
+        assigned 'com.tailrecursion.blacktide.compiler.languages/Assigned-Expr]
+    (probes/accepts? 'com.tailrecursion.blacktide.compiler.languages/Bound-Node {:op :setq :pairs []} true)
+    (probes/accepts? 'com.tailrecursion.blacktide.compiler.languages/Sequential-Node {:op :setq :pairs []} false)
+    (probes/accepts? 'com.tailrecursion.blacktide.compiler.languages/Sequential-Node batch true)
     (probes/accepts? assigned (expr batch) false)
     (probes/accepts? assigned (expr {:op :do :forms [(expr batch)]}) false)
-    (probes/accepts? 'com.tailrecursion.blacktide.compiler.source-languages/Native-Expr
+    (probes/accepts? 'com.tailrecursion.blacktide.compiler.languages/Native-Expr
                     (expr {:op :do :forms [(expr batch)]}) false)
     (probes/accepts? assigned (expr {:op :literal :value batch}) true)))
 
 (deftest native-and-recur-boundaries
-  (probes/accepts? 'com.tailrecursion.blacktide.compiler.source-languages/Native-Node {:op :setq :pairs []} false)
-  (probes/accepts? 'com.tailrecursion.blacktide.compiler.source-languages/Native-Node
+  (probes/accepts? 'com.tailrecursion.blacktide.compiler.languages/Native-Node {:op :setq :pairs []} false)
+  (probes/accepts? 'com.tailrecursion.blacktide.compiler.languages/Native-Node
                   {:op :goto :name 'done} false)
-  (probes/accepts? 'com.tailrecursion.blacktide.compiler.source-languages/Ordered-Node
+  (probes/accepts? 'com.tailrecursion.blacktide.compiler.languages/Ordered-Node
                   {:op :recur :target {:id 'loop :bindings []} :args []} true)
-  (probes/accepts? 'com.tailrecursion.blacktide.compiler.source-languages/Ordered-Node
+  (probes/accepts? 'com.tailrecursion.blacktide.compiler.languages/Ordered-Node
                   {:op :recur :target {:id 'loop :bindings []} :args [{:op :literal :value 1}]} false))
 
 (deftest host-preparation-boundary
   (let [operation {:op :method-value :name 'String/.length}
         unprepared {:op :host :operation operation :args []}
         prepared {:op :host-call :operation operation :args []}]
-    (probes/accepts? 'com.tailrecursion.blacktide.compiler.source-languages/Collections-Node unprepared true)
-    (probes/accepts? 'com.tailrecursion.blacktide.compiler.source-languages/Prepared-Node unprepared false)
-    (probes/accepts? 'com.tailrecursion.blacktide.compiler.source-languages/Prepared-Node prepared true)
-    (probes/accepts? 'com.tailrecursion.blacktide.compiler.source-languages/Ordered-Node
+    (probes/accepts? 'com.tailrecursion.blacktide.compiler.languages/Collections-Node unprepared true)
+    (probes/accepts? 'com.tailrecursion.blacktide.compiler.languages/Prepared-Node unprepared false)
+    (probes/accepts? 'com.tailrecursion.blacktide.compiler.languages/Prepared-Node prepared true)
+    (probes/accepts? 'com.tailrecursion.blacktide.compiler.languages/Ordered-Node
                     (assoc prepared :args [{:op :literal :value nil}]) false)))
 
 (deftest case-selection-boundary
@@ -79,8 +49,8 @@
               :jvm {:hint nil :var-meta {} :type Long/TYPE :primitive? true :hint-error nil}
               :node {:op :literal :value 0}}
         node {:op :case :selector expr :arms [expr]}]
-    (probes/accepts? 'com.tailrecursion.blacktide.compiler.source-languages/Assigned-Node node true)
-    (probes/accepts? 'com.tailrecursion.blacktide.compiler.source-languages/Cases-Node node false)
-    (probes/accepts? 'com.tailrecursion.blacktide.compiler.source-languages/Cases-Node (assoc node :op :select) true)
+    (probes/accepts? 'com.tailrecursion.blacktide.compiler.languages/Assigned-Node node true)
+    (probes/accepts? 'com.tailrecursion.blacktide.compiler.languages/Cases-Node node false)
+    (probes/accepts? 'com.tailrecursion.blacktide.compiler.languages/Cases-Node (assoc node :op :select) true)
     (probes/accepts? 'com.tailrecursion.blacktide.compiler.languages/CFG-Term
                     {:op :switch :test {:op :literal :value 0} :targets [1 2]} true)))
diff --git a/test/com/tailrecursion/blacktide/collections_test.clj b/test/com/tailrecursion/blacktide/collections_test.clj
index ac55e22..95eb0c0 100644
--- a/test/com/tailrecursion/blacktide/collections_test.clj
+++ b/test/com/tailrecursion/blacktide/collections_test.clj
@@ -5,8 +5,8 @@
             [clojure.test.check.generators :as gen]
             [clojure.test.check.properties :as prop]
             [com.tailrecursion.blacktide :refer [loophole]]
-            [com.tailrecursion.blacktide.compiler.backend :as backend]
-            [com.tailrecursion.blacktide.compiler.emit :as emit]))
+            [com.tailrecursion.blacktide.compiler :as compiler]
+            [com.tailrecursion.blacktide.compiler.passes.emit :as emit]))
 
 (defn record! [events tag value] (swap! events conj tag) value)
 (defmacro key-a [] :a)
@@ -88,9 +88,9 @@
 (deftest vector-construction-boundaries
   (doseq [mode [:default :dispatcher]]
     (binding [*ns* (the-ns 'com.tailrecursion.blacktide.collections-test)]
-      (with-redefs [backend/compile-program (if (= mode :dispatcher)
+      (with-redefs [compiler/compile-program (if (= mode :dispatcher)
                                                   emit/program-form
-                                                  backend/compile-program)]
+                                                  compiler/compile-program)]
         (doseq [size [0 1 6 7 32 33 63] metadata [nil {} {:note :yes}]]
           (let [body (with-meta (mapv #(list 'record! 'events % %) (range size)) metadata)
                 wrap identity
diff --git a/test/com/tailrecursion/blacktide/generated_test.clj b/test/com/tailrecursion/blacktide/generated_test.clj
index 7327ae6..823bad3 100644
--- a/test/com/tailrecursion/blacktide/generated_test.clj
+++ b/test/com/tailrecursion/blacktide/generated_test.clj
@@ -8,8 +8,8 @@
             [clojure.walk :as walk]
             [com.tailrecursion.blacktide :refer [loophole label goto setq psetq]]
             [com.tailrecursion.blacktide.backend-test :as backend]
-            [com.tailrecursion.blacktide.compiler.structure :as structure]
-            [com.tailrecursion.blacktide.compiler.storage :as storage]))
+            [com.tailrecursion.blacktide.compiler.passes.structure :as structure]
+            [com.tailrecursion.blacktide.compiler.passes.storage :as storage]))
 
 (defn three-steps [x k] (+ x (* 3 k)))
 
diff --git a/test/com/tailrecursion/blacktide/graph_test.clj b/test/com/tailrecursion/blacktide/graph_test.clj
index f0d93b5..653c6c0 100644
--- a/test/com/tailrecursion/blacktide/graph_test.clj
+++ b/test/com/tailrecursion/blacktide/graph_test.clj
@@ -4,7 +4,7 @@
             [clojure.test.check.clojure-test :refer [defspec]]
             [clojure.test.check.generators :as gen]
             [clojure.test.check.properties :as prop]
-            [com.tailrecursion.blacktide.compiler.graph :as graph]
+            [com.tailrecursion.blacktide.compiler.passes.graph :as graph]
             [com.tailrecursion.blacktide.machine-test :as m]))
 
 (defn jump [target] {:op :jump :target target})
diff --git a/test/com/tailrecursion/blacktide/machine_test.clj b/test/com/tailrecursion/blacktide/machine_test.clj
index 128393a..f003755 100644
--- a/test/com/tailrecursion/blacktide/machine_test.clj
+++ b/test/com/tailrecursion/blacktide/machine_test.clj
@@ -2,7 +2,7 @@
   "Compile allocated machines with the real Clojure compiler and execute them."
   (:require [clojure.test :refer [deftest is]]
             [com.tailrecursion.blacktide.compiler.jvm :as jvm]
-            [com.tailrecursion.blacktide.compiler.emit :as emit]))
+            [com.tailrecursion.blacktide.compiler.passes.emit :as emit]))
 
 (defn literal [value] {:op :literal :value value})
 (defn local [id] {:op :local :id id})
diff --git a/test/com/tailrecursion/blacktide/source_loops_test.clj b/test/com/tailrecursion/blacktide/source_loops_test.clj
index 4a88103..c5c64dc 100644
--- a/test/com/tailrecursion/blacktide/source_loops_test.clj
+++ b/test/com/tailrecursion/blacktide/source_loops_test.clj
@@ -7,8 +7,8 @@
             [clojure.test.check.properties :as prop]
             [clojure.walk :as walk]
             [com.tailrecursion.blacktide :refer [loophole]]
-            [com.tailrecursion.blacktide.compiler.backend :as backend]
-            [com.tailrecursion.blacktide.compiler.emit :as emit]
+            [com.tailrecursion.blacktide.compiler :as compiler]
+            [com.tailrecursion.blacktide.compiler.passes.emit :as emit]
             [com.tailrecursion.blacktide.jvm-test :as jvm]))
 
 (defn root-error [e] (if-let [cause (ex-cause e)] (recur cause) e))
@@ -40,9 +40,9 @@
 (defn compile-body [body mode]
   (try
     (binding [*ns* (the-ns 'com.tailrecursion.blacktide.source-loops-test)]
-      (with-redefs [backend/compile-program (if (= mode :dispatcher)
+      (with-redefs [compiler/compile-program (if (= mode :dispatcher)
                                             emit/program-form
-                                            backend/compile-program)]
+                                            compiler/compile-program)]
         (eval (list 'fn '[events input n] (enclose body mode)))))
     (catch Throwable e {:compile (class (root-error e))})))
 (defn outcome [f input n]
diff --git a/test/com/tailrecursion/nanopass/check.clj b/test/com/tailrecursion/nanopass/check.clj
index 7c3befa..8c8149a 100644
--- a/test/com/tailrecursion/nanopass/check.clj
+++ b/test/com/tailrecursion/nanopass/check.clj
@@ -87,6 +87,28 @@
         (is (or (nil? ex) (= :top-level-error (:type-error (ex-data ex)))) (str ex))
         (is (= accepted (empty? errors)) (pr-str [form errors]))))))
 
+(deftest recursive-processor-bodies-are-checked
+  (doseq [[accepted value] [[true '(:value node)] [false 'node]]]
+    (let [definitions
+          (concat
+           [(list 'expression '{:from [f/Source Binding] :to [f/Core Binding] :body true}
+                  '[node] '(binding node))
+            (list 'binding '{:from [f/Source Binding] :to [f/Core Binding]}
+                  '[node] :binding {:op :binding :name '(:name node) :value (list 'p/lower value)})]
+           ;; Distinct contracts across the array-map/hash-map boundary catch
+           ;; positional annotations attached to the wrong letfn processor.
+           (for [i (range 9)]
+             (list (symbol (str "constant" i)) {:from '[f/Source Expr] :to (list 't/Val i) :body true}
+                   '[node] i)))
+          [head bindings signatures]
+          (binding [*ns* (the-ns 'com.tailrecursion.nanopass.check)]
+            (macroexpand-1 (cons 'np/defpasses definitions)))
+          form (list head bindings signatures 'expression)
+          {:keys [ex delayed-errors]} (check-form form '[f/Source-Binding :-> f/Core-Binding])
+          errors (concat delayed-errors (:errors (ex-data ex)))]
+      (is (or (nil? ex) (= :top-level-error (:type-error (ex-data ex)))) (str ex))
+      (is (= accepted (empty? errors)) (pr-str errors)))))
+
 (deftest assignment-contract-probes
   (doseq [form
           '[(fn [node] node)
diff --git a/test/com/tailrecursion/nanopass/clients/assignment.clj b/test/com/tailrecursion/nanopass/clients/assignment.clj
index 1090a9f..e090ffb 100644
--- a/test/com/tailrecursion/nanopass/clients/assignment.clj
+++ b/test/com/tailrecursion/nanopass/clients/assignment.clj
@@ -22,26 +22,25 @@
                (t/HMap :mandatory {:op ':temp :id t/Sym} :complete? true)
                (t/HMap :mandatory {:op ':bind :id t/Sym :value Expr :body Expr} :complete? true)]}})
 
-(declare lower)
+(np/defpasses
+  (sequential-writes {:from [Source Writes] :to [Core Expr] :body true}
+    [writes]
+    {:op :do :forms (mapv (fn [[target rhs]] {:op :write :binding target :value (lower rhs)}) writes)})
 
-(np/defpass sequential-writes {:from [Source Writes] :to [Core Expr] :body true}
-  [writes]
-  {:op :do :forms (mapv (fn [[target rhs]] {:op :write :binding target :value (lower rhs)}) writes)})
+  (parallel-writes {:from [Source Writes] :to [Core Expr] :body true}
+    [writes]
+    (let [names (mapv (fn [_] (gensym "rhs__")) writes)
+          body {:op :do
+                :forms (conj (mapv (fn [[target _] id]
+                                    {:op :write :binding target :value {:op :temp :id id}})
+                                  writes names)
+                             {:op :const :value nil})}]
+      (reduce (fn [^{::t/- Core-Expr} body ^{::t/- (t/HVec [t/Sym Source-Expr])} entry]
+                (let [[id rhs] entry]
+                  {:op :bind :id id :value (lower rhs) :body body}))
+              body (reverse (mapv (fn [id [_ rhs]] [id rhs]) names writes)))))
 
-(np/defpass parallel-writes {:from [Source Writes] :to [Core Expr] :body true}
-  [writes]
-  (let [names (mapv (fn [_] (gensym "rhs__")) writes)
-        body {:op :do
-              :forms (conj (mapv (fn [[target _] id]
-                                  {:op :write :binding target :value {:op :temp :id id}})
-                                writes names)
-                           {:op :const :value nil})}]
-    (reduce (fn [^{::t/- Core-Expr} body ^{::t/- (t/HVec [t/Sym Source-Expr])} entry]
-              (let [[id rhs] entry]
-                {:op :bind :id id :value (lower rhs) :body body}))
-            body (reverse (mapv (fn [id [_ rhs]] [id rhs]) names writes)))))
-
-(np/defpass lower {:from [Source Expr] :to [Core Expr]}
-  [node]
-  :setq (sequential-writes (:writes node))
-  :psetq (parallel-writes (:writes node)))
+  (lower {:from [Source Expr] :to [Core Expr]}
+    [node]
+    :setq (sequential-writes (:writes node))
+    :psetq (parallel-writes (:writes node))))
diff --git a/test/com/tailrecursion/nanopass/clients/flow.clj b/test/com/tailrecursion/nanopass/clients/flow.clj
index 6d5b683..582b71e 100644
--- a/test/com/tailrecursion/nanopass/clients/flow.clj
+++ b/test/com/tailrecursion/nanopass/clients/flow.clj
@@ -5,47 +5,46 @@
             [com.tailrecursion.nanopass.clients.flow-languages :as l]
             [typed.clojure :as-alias t]))
 
-(declare bind-expr bind-exprs bind-local lower lower-exprs)
+(np/defpasses
+  (bind-exprs
+    {:from [l/Syntax Exprs] :to (t/HVec [t/Int (t/Vec l/Bound-Expr)])
+     :args [(t/Map t/Sym t/Sym) t/Int] :body true}
+    [env supply forms]
+    (reduce (fn [^{::t/- (t/HVec [t/Int (t/Vec l/Bound-Expr)])} acc ^{::t/- l/Syntax-Expr} form]
+              (let [[supply out] acc [supply form] (bind-expr env supply form)]
+                [supply (conj out form)])) [supply []] forms))
 
-(np/defpass bind-exprs
-  {:from [l/Syntax Exprs] :to (t/HVec [t/Int (t/Vec l/Bound-Expr)])
-   :args [(t/Map t/Sym t/Sym) t/Int] :body true}
-  [env supply forms]
-  (reduce (fn [^{::t/- (t/HVec [t/Int (t/Vec l/Bound-Expr)])} acc ^{::t/- l/Syntax-Expr} form]
-            (let [[supply out] acc [supply form] (bind-expr env supply form)]
-              [supply (conj out form)])) [supply []] forms))
+  (bind-local
+    {:from [l/Syntax Binding] :to (t/HVec [t/Int l/Bound-Binding])
+     :args [(t/Map t/Sym t/Sym) t/Int] :body true}
+    [env supply node]
+    (let [[supply init] (bind-expr env supply (:value node))]
+      [(inc supply) {:op :binding :id (symbol (str "local__" supply)) :value init}]))
 
-(np/defpass bind-local
-  {:from [l/Syntax Binding] :to (t/HVec [t/Int l/Bound-Binding])
-   :args [(t/Map t/Sym t/Sym) t/Int] :body true}
-  [env supply node]
-  (let [[supply init] (bind-expr env supply (:value node))]
-    [(inc supply) {:op :binding :id (symbol (str "local__" supply)) :value init}]))
-
-(np/defpass bind-expr
-  {:from [l/Syntax Expr] :to (t/HVec [t/Int l/Bound-Expr]) :args [(t/Map t/Sym t/Sym) t/Int]}
-  [env supply node]
-  :int [supply node]
-  :read [supply {:op :ref :id (or (get env (:name node)) (throw (ex-info "Unbound name" {:name (:name node)})))}]
-  :set (let [id (or (get env (:name node)) (throw (ex-info "Unbound name" {:name (:name node)})))
-             [supply value] (bind-expr env supply (:value node))]
-         [supply {:op :write :id id :value value}])
-  :plus (let [[supply left] (bind-expr env supply (:left node))
-              [supply right] (bind-expr env supply (:right node))]
-          [supply {:op :plus :left left :right right}])
-  :let (let [[supply local] (bind-local env supply (:binding node))
-             env (assoc env (:name (:binding node)) (:id local))
-             [supply body] (bind-expr env supply (:body node))]
-         [supply {:op :let :binding local :body body}])
-  :if (let [[supply test] (bind-expr env supply (:test node))
-            [supply yes] (bind-expr env supply (:then node))
-            [supply no] (bind-expr env supply (:else node))]
-        [supply {:op :if :test test :then yes :else no}])
-  :while (let [[supply test] (bind-expr env supply (:test node))
+  (bind-expr
+    {:from [l/Syntax Expr] :to (t/HVec [t/Int l/Bound-Expr]) :args [(t/Map t/Sym t/Sym) t/Int]}
+    [env supply node]
+    :int [supply node]
+    :read [supply {:op :ref :id (or (get env (:name node)) (throw (ex-info "Unbound name" {:name (:name node)})))}]
+    :set (let [id (or (get env (:name node)) (throw (ex-info "Unbound name" {:name (:name node)})))
+               [supply value] (bind-expr env supply (:value node))]
+           [supply {:op :write :id id :value value}])
+    :plus (let [[supply left] (bind-expr env supply (:left node))
+                [supply right] (bind-expr env supply (:right node))]
+            [supply {:op :plus :left left :right right}])
+    :let (let [[supply local] (bind-local env supply (:binding node))
+               env (assoc env (:name (:binding node)) (:id local))
                [supply body] (bind-expr env supply (:body node))]
-           [supply {:op :while :test test :body body}])
-  :do (let [[supply forms] (bind-exprs env supply (:forms node))]
-        [supply {:op :do :forms forms}]))
+           [supply {:op :let :binding local :body body}])
+    :if (let [[supply test] (bind-expr env supply (:test node))
+              [supply yes] (bind-expr env supply (:then node))
+              [supply no] (bind-expr env supply (:else node))]
+          [supply {:op :if :test test :then yes :else no}])
+    :while (let [[supply test] (bind-expr env supply (:test node))
+                 [supply body] (bind-expr env supply (:body node))]
+             [supply {:op :while :test test :body body}])
+    :do (let [[supply forms] (bind-exprs env supply (:forms node))]
+          [supply {:op :do :forms forms}])))
 
 (np/defpass fresh-block {:from [l/Machine Builder] :to (t/HVec [l/Machine-Builder t/Int]) :body true}
   [state]
@@ -67,43 +66,44 @@
   [id state]
   (assoc state :current id :ops []))
 
-(np/defpass lower-exprs
-  {:from [l/Bound Exprs] :to (t/HVec [l/Machine-Builder l/Machine-Operand]) :args [l/Machine-Builder] :body true}
-  [state forms]
-  (reduce (fn [^{::t/- (t/HVec [l/Machine-Builder l/Machine-Operand])} acc ^{::t/- l/Bound-Expr} form]
-            (lower (first acc) form)) [state {:op :literal :value 0}] forms))
+(np/defpasses
+  (lower-exprs
+    {:from [l/Bound Exprs] :to (t/HVec [l/Machine-Builder l/Machine-Operand]) :args [l/Machine-Builder] :body true}
+    [state forms]
+    (reduce (fn [^{::t/- (t/HVec [l/Machine-Builder l/Machine-Operand])} acc ^{::t/- l/Bound-Expr} form]
+              (lower (first acc) form)) [state {:op :literal :value 0}] forms))
 
-(np/defpass lower
-  {:from [l/Bound Expr] :to (t/HVec [l/Machine-Builder l/Machine-Operand]) :args [l/Machine-Builder]}
-  [state node]
-  :int [state {:op :literal :value (:value node)}]
-  :ref (let [[state id] (fresh-value state)]
-         [(emit-op state {:op :put :id id :value {:op :register :id (:id node)}}) {:op :register :id id}])
-  :write (let [[state value] (lower state (:value node))]
-           [(emit-op state {:op :put :id (:id node) :value value}) value])
-  :plus (let [[state left] (lower state (:left node))
-              [state right] (lower state (:right node))
-              [state id] (fresh-value state)]
-          [(emit-op state {:op :add :id id :left left :right right}) {:op :register :id id}])
-  :let (let [local (:binding node) [state init] (lower state (:value local))]
-         (lower (emit-op state {:op :put :id (:id local) :value init}) (:body node)))
-  :do (lower-exprs state (:forms node))
-  :if (let [[state test] (lower state (:test node))
-            [state yes] (fresh-block state) [state no] (fresh-block state) [state join] (fresh-block state)
-            [state result] (fresh-value state)
-            state (seal state {:op :branch :test test :yes yes :no no})
-            [state a] (lower (start yes state) (:then node))
-            state (seal (emit-op state {:op :put :id result :value a}) {:op :jump :target join})
-            [state b] (lower (start no state) (:else node))
-            state (seal (emit-op state {:op :put :id result :value b}) {:op :jump :target join})]
-        [(start join state) {:op :register :id result}])
-  :while (let [[state header] (fresh-block state) [state body] (fresh-block state) [state exit] (fresh-block state)
-               state (seal state {:op :jump :target header})
-               [state test] (lower (start header state) (:test node))
-               state (seal state {:op :branch :test test :yes body :no exit})
-               [state _] (lower (start body state) (:body node))
-               state (seal state {:op :jump :target header})]
-           [(start exit state) {:op :literal :value 0}]))
+  (lower
+    {:from [l/Bound Expr] :to (t/HVec [l/Machine-Builder l/Machine-Operand]) :args [l/Machine-Builder]}
+    [state node]
+    :int [state {:op :literal :value (:value node)}]
+    :ref (let [[state id] (fresh-value state)]
+           [(emit-op state {:op :put :id id :value {:op :register :id (:id node)}}) {:op :register :id id}])
+    :write (let [[state value] (lower state (:value node))]
+             [(emit-op state {:op :put :id (:id node) :value value}) value])
+    :plus (let [[state left] (lower state (:left node))
+                [state right] (lower state (:right node))
+                [state id] (fresh-value state)]
+            [(emit-op state {:op :add :id id :left left :right right}) {:op :register :id id}])
+    :let (let [local (:binding node) [state init] (lower state (:value local))]
+           (lower (emit-op state {:op :put :id (:id local) :value init}) (:body node)))
+    :do (lower-exprs state (:forms node))
+    :if (let [[state test] (lower state (:test node))
+              [state yes] (fresh-block state) [state no] (fresh-block state) [state join] (fresh-block state)
+              [state result] (fresh-value state)
+              state (seal state {:op :branch :test test :yes yes :no no})
+              [state a] (lower (start yes state) (:then node))
+              state (seal (emit-op state {:op :put :id result :value a}) {:op :jump :target join})
+              [state b] (lower (start no state) (:else node))
+              state (seal (emit-op state {:op :put :id result :value b}) {:op :jump :target join})]
+          [(start join state) {:op :register :id result}])
+    :while (let [[state header] (fresh-block state) [state body] (fresh-block state) [state exit] (fresh-block state)
+                 state (seal state {:op :jump :target header})
+                 [state test] (lower (start header state) (:test node))
+                 state (seal state {:op :branch :test test :yes body :no exit})
+                 [state _] (lower (start body state) (:body node))
+                 state (seal state {:op :jump :target header})]
+             [(start exit state) {:op :literal :value 0}])))
 
 (np/defpass compile-program {:from [l/Syntax Expr] :to [l/Machine Graph] :body true}
   [source]
diff --git a/test/com/tailrecursion/nanopass/pass_test.clj b/test/com/tailrecursion/nanopass/pass_test.clj
index 56d9de2..2a835da 100644
--- a/test/com/tailrecursion/nanopass/pass_test.clj
+++ b/test/com/tailrecursion/nanopass/pass_test.clj
@@ -48,7 +48,10 @@
                              [node] node node)]
             [:invalid-pass (np/defpass broken {:from [f/Core Expr] :to [f/Core Expr] :body :yes}
                              [node] node)]
-            [:invalid-pass (np/defpass broken {:from [f/Core Expr]} [node] node)]]]
+            [:invalid-pass (np/defpass broken {:from [f/Core Expr]} [node] node)]
+            [:invalid-pass (np/defpasses)]
+            [:invalid-pass (np/defpasses (same {:from [f/Core Expr] :to [f/Core Expr]} [node])
+                                        (same {:from [f/Core Expr] :to [f/Core Expr]} [node]))]]]
     (is (= kind (binding [*ns* (the-ns 'com.tailrecursion.nanopass.passes)]
                   (try (macroexpand-1 form) nil
                        (catch Exception e (-> (or (ex-cause e) e) ex-data :kind)))))
diff --git a/test/com/tailrecursion/nanopass/passes.clj b/test/com/tailrecursion/nanopass/passes.clj
index e6eebf8..9272bcb 100644
--- a/test/com/tailrecursion/nanopass/passes.clj
+++ b/test/com/tailrecursion/nanopass/passes.clj
@@ -4,31 +4,29 @@
             [com.tailrecursion.nanopass.fixtures :as f]
             [typed.clojure :as-alias t]))
 
-(declare lower lower-binding)
-
-(np/defpass lower
-  {:from [f/Source Expr] :to [f/Core Expr] :processors {Binding lower-binding}}
-  [node]
-  :assign {:op :write :name (:name (:binding node)) :value (lower (:value node))})
-
-(np/defpass lower-binding {:from [f/Source Binding] :to [f/Core Binding]
-                           :processors {Expr lower}}
-  [node])
-
-(declare rename rename-binding)
-
-(np/defpass rename
-  {:from [f/Core Expr] :to [f/Core Expr] :args [(t/Map t/Sym t/Sym)]
-   :processors {Binding rename-binding}}
-  [env node]
-  :read {:op :read :name (get env (:name node) (:name node))}
-  :write {:op :write :name (get env (:name node) (:name node)) :value (rename env (:value node))})
-
-(np/defpass rename-binding
-  {:from [f/Core Binding] :to [f/Core Binding] :args [(t/Map t/Sym t/Sym)]
-   :processors {Expr rename}}
-  [env node]
-  :binding {:op :binding :name (get env (:name node) (:name node)) :value (rename env (:value node))})
+(np/defpasses
+  (lower
+    {:from [f/Source Expr] :to [f/Core Expr] :processors {Binding lower-binding}}
+    [node]
+    :assign {:op :write :name (:name (:binding node)) :value (lower (:value node))})
+
+  (lower-binding {:from [f/Source Binding] :to [f/Core Binding]
+                             :processors {Expr lower}}
+    [node]))
+
+(np/defpasses
+  (rename
+    {:from [f/Core Expr] :to [f/Core Expr] :args [(t/Map t/Sym t/Sym)]
+     :processors {Binding rename-binding}}
+    [env node]
+    :read {:op :read :name (get env (:name node) (:name node))}
+    :write {:op :write :name (get env (:name node) (:name node)) :value (rename env (:value node))})
+
+  (rename-binding
+    {:from [f/Core Binding] :to [f/Core Binding] :args [(t/Map t/Sym t/Sym)]
+     :processors {Expr rename}}
+    [env node]
+    :binding {:op :binding :name (get env (:name node) (:name node)) :value (rename env (:value node))}))
 
 (np/defpass count-step
   {:from [f/Core Expr] :to (t/HVec [t/Int f/Core-Expr]) :args [t/Int] :body true}