git » blacktide.git » commit 240f2b4

Remove runtime lookup emulation and redundant compiler machinery

author Alan Dipert
2026-11-11 14:29:22 UTC
committer Alan Dipert
2026-11-11 14:29:22 UTC
parent 786d596043105d75b97684573be22a166b36159d

Remove runtime lookup emulation and redundant compiler machinery

Emit literal keyword calls as ordinary Clojure after operand normalization.
Keep their cached dispatch distinct from indirect keyword invocation, but
stop promising exact cache snapshots, installation counts, or miss probes.
Delete KeywordSite, its macro, Java preparation/build steps, and packaging
requirements. Document the boundary for custom cache-sensitive lookups.

Allow structured recovery to duplicate compiler-generated method wrappers
and native subtrees without preserving their concrete class layout. Keep
bound-reference identity, execution counts, source validation, and the code
size budget. Remove the construction-site guard and its block-visit records.

Fix a pre-existing representation bug uncovered while challenging the box
origin analysis. An observed operand can become boxed during the storage
fixed point, changing a numeric plan's result from primitive to reference.
The target previously kept its obsolete primitive representation. For
example, saving x after computing (/ x 1), where x is a computed Double NaN
and the result slot starts numerically, could detach the returned reference
from the saved value and change both identical? and =.

Require a numeric plan's result representation to match its target storage
on every solver iteration. Otherwise retain the target and possible source
references in boxed storage. With this producer constraint and existing
observed-copy propagation, value inference already establishes eligibility
for return boxing; remove the separate box-origin fixed point. Literal pool
sharing was already unspecified. Preserve source primitive-read boxing,
external references, numeric widths, and observable aliasing.

Fold the unused Structured grammar base into Joined and make CFG fact
collection accept block maps instead of a redundant whole-program shape.
Remove the second, toy assignment compiler and its duplicate tests/checker
probes. Retain the production assignment oracle, generator seeds, shrinking,
and independent nanopass checks. Remove unused oracle branches and the
check alias's now-unneeded test.check dependency. Replace incidental cache,
wrapper-class and JVM-message assertions with the semantic observations
required by the contract; HotSpot can omit built-in exception messages.

Relevant Clojure implementation references:
- Compiler.KeywordInvokeExpr and QualifiedMethodExpr:
  https://github.com/clojure/clojure/blob/clojure-1.12.6/src/jvm/clojure/lang/Compiler.java
- Keyword cache installation and receiver guards:
  https://github.com/clojure/clojure/blob/clojure-1.12.6/src/jvm/clojure/lang/KeywordLookupSite.java
- Numeric overloads and reference-preserving NaN division:
  https://github.com/clojure/clojure/blob/clojure-1.12.6/src/jvm/clojure/lang/Numbers.java

Validation:
- Full suite on JDK 17, 21, 25 and 27: 143 tests / 7,311 assertions each;
  zero failures or errors, including 2,400 control-program variants per JDK
  with seed 618033 and the remaining deterministic generative regimes.
- Typed Clojure on JDK 25 and 27: 14 tests / 144 assertions each; all pass.
- Reproduced the NaN bug on untouched parent source; added regressions and
  checked 108 additional observed-reference/selection/join/loop combinations.
- Source-only JAR, fresh local/JAR clients, source-free AOT and execution
  after removing Blacktide compiler classes pass. JAR matches source and
  contains no runtime helpers or compiled classes.
- git diff --check passes. Net reduction: 436 lines.

.forgejo/workflows/check.yml +1 -3
README.md +2 -5
bin/check +0 -1
build.clj +2 -9
deps.edn +2 -5
doc/language.md +2 -2
src-java/com/tailrecursion/blacktide/runtime/KeywordSite.java +0 -40
src/com/tailrecursion/blacktide/compiler/jvm.clj +2 -2
src/com/tailrecursion/blacktide/compiler/languages.clj +14 -17
src/com/tailrecursion/blacktide/compiler/passes/boundaries.clj +0 -7
src/com/tailrecursion/blacktide/compiler/passes/box_origins.clj +0 -56
src/com/tailrecursion/blacktide/compiler/passes/collections.clj +1 -1
src/com/tailrecursion/blacktide/compiler/passes/graph.clj +5 -5
src/com/tailrecursion/blacktide/compiler/passes/host_emit.clj +1 -2
src/com/tailrecursion/blacktide/compiler/passes/joins.clj +1 -1
src/com/tailrecursion/blacktide/compiler/passes/native.clj +2 -6
src/com/tailrecursion/blacktide/compiler/passes/source.clj +3 -19
src/com/tailrecursion/blacktide/compiler/passes/storage.clj +17 -17
src/com/tailrecursion/blacktide/compiler/passes/structure.clj +15 -46
src/com/tailrecursion/blacktide/compiler/passes/value_facts.clj +2 -2
src/com/tailrecursion/blacktide/runtime.clj +0 -7
test/com/tailrecursion/assignment_model.clj +0 -76
test/com/tailrecursion/blacktide/aot_test.clj +14 -5
test/com/tailrecursion/blacktide/assignment_model.clj +53 -0
test/com/tailrecursion/blacktide/assignment_test.clj +3 -3
test/com/tailrecursion/blacktide/backend_test.clj +11 -15
test/com/tailrecursion/blacktide/generated_test.clj +5 -1
test/com/tailrecursion/blacktide/interop_values_test.clj +50 -0
test/com/tailrecursion/blacktide/jvm_fixture.clj +1 -15
test/com/tailrecursion/blacktide/lookup_test.clj +40 -70
test/com/tailrecursion/blacktide/native_sites_test.clj +0 -101
test/com/tailrecursion/blacktide/numeric_test.clj +10 -3
test/com/tailrecursion/blacktide/package_test.clj +4 -4
test/com/tailrecursion/blacktide/source_loops_test.clj +1 -10
test/com/tailrecursion/nanopass/annotations.clj +1 -4
test/com/tailrecursion/nanopass/assignment_test.clj +0 -64
test/com/tailrecursion/nanopass/check.clj +2 -32
test/com/tailrecursion/nanopass/clients/assignment.clj +0 -46
test/com/tailrecursion/test_runner.clj +1 -2

diff --git a/.forgejo/workflows/check.yml b/.forgejo/workflows/check.yml
index 3bfd93b..b1796b4 100644
--- a/.forgejo/workflows/check.yml
+++ b/.forgejo/workflows/check.yml
@@ -41,9 +41,7 @@ jobs:
           bash linux-install-1.12.4.1618.sh --prefix "$RUNNER_TEMP/clojure"
           echo "$RUNNER_TEMP/clojure/bin" >> "$GITHUB_PATH"
       - name: Test
-        run: |
-          clojure -T:build compile-java
-          clojure -M:test
+        run: clojure -M:test
       - name: Check pass types
         if: matrix.java == '25' || matrix.java == '27'
         run: clojure -M:check
diff --git a/README.md b/README.md
index 608dd15..cfe7895 100644
--- a/README.md
+++ b/README.md
@@ -52,18 +52,15 @@ To use that neighboring checkout, add this dependency to your project's `deps.ed
 {:deps {com.tailrecursion/blacktide {:local/root "../blacktide"}}}
 ```
 
-Run `clojure -X:deps prep` in the client project to compile the bundled JVM keyword-cache helper. Library JARs already include the helper.
-
-Clojure is the only runtime dependency. Typed Clojure checks compiler passes during development.
+Clojure is the only dependency. Generated code needs no Blacktide runtime helpers. Typed Clojure checks compiler passes during development.
 
 ## Develop
 
 | Command | Purpose |
 | --- | --- |
-| `clojure -T:build compile-java` | Compile the Java helper after checkout or changes to `src-java`. |
 | `clojure -M:test` | Run semantic, generative, differential and AOT tests. |
 | `clojure -M:check` | Check language contracts and production pass bodies. |
-| `bin/check` | Run all three steps above; accepts CLI `-S` options. |
+| `bin/check` | Run tests and type checks; accepts CLI `-S` options. |
 | `clojure -T:build jar` | Build `target/blacktide-0.1.0-SNAPSHOT.jar`. |
 
 Select a JDK with `JAVA_CMD=/path/to/jdk/bin/java bin/check`. Development targets JDK 25/27; 17/21 are compatibility targets. Forgejo CI tests all four JDKs, checks pass types on 25/27, and builds the JAR.
diff --git a/bin/check b/bin/check
index f6bf105..5ff27b4 100755
--- a/bin/check
+++ b/bin/check
@@ -1,6 +1,5 @@
 #!/usr/bin/env bash
 set -euo pipefail
 cd "$(dirname "$0")/.."
-clojure "$@" -T:build compile-java
 clojure "$@" -M:test
 clojure "$@" -M:check
diff --git a/build.clj b/build.clj
index 53402b1..b0f0f75 100644
--- a/build.clj
+++ b/build.clj
@@ -1,20 +1,13 @@
 (ns build
-  "Build the JVM helper and a source library JAR; the compiler stays in Clojure."
+  "Package the Clojure source library and its resources."
   (:require [clojure.tools.build.api :as b]
             [clojure.java.io :as io]))
 
-(defn compile-java [_]
-  (b/delete {:path "target/classes"})
-  (b/javac {:src-dirs ["src-java"] :class-dir "target/classes"
-            :basis (b/create-basis {:project "deps.edn"})
-            :javac-opts ["--release" "17" "-Xlint:all" "-Werror"]}))
-
 (defn jar [{:keys [version] :or {version "0.1.0-SNAPSHOT"}}]
-  (compile-java nil)
   (let [lib 'com.tailrecursion/blacktide class-dir "target/jar-classes"
         basis (b/create-basis {:project "deps.edn"})]
     (b/delete {:path class-dir})
-    (b/copy-dir {:src-dirs ["src" "resources" "target/classes"] :target-dir class-dir})
+    (b/copy-dir {:src-dirs ["src" "resources"] :target-dir class-dir})
     ;; Editor state may be nested beneath namespace directories.
     (doseq [file (file-seq (io/file class-dir))
             :when (.startsWith (.getName file) ".")]
diff --git a/deps.edn b/deps.edn
index bb54034..e2e184c 100644
--- a/deps.edn
+++ b/deps.edn
@@ -1,7 +1,5 @@
-{:paths ["src" "resources" "target/classes"]
+{:paths ["src" "resources"]
  :deps {org.clojure/clojure {:mvn/version "1.12.6"}}
- :deps/prep-lib {:alias :build :fn compile-java
-                 :ensure "target/classes/com/tailrecursion/blacktide/runtime/KeywordSite.class"}
  :aliases
  {:build {:deps {io.github.clojure/tools.build {:mvn/version "0.10.14"}}
           :ns-default build}
@@ -10,7 +8,6 @@
          :jvm-opts ["-Xmx768m"]
          :main-opts ["-m" "com.tailrecursion.test-runner"]}
   :check {:extra-paths ["test" "test-resources"]
-          :extra-deps {org.typedclojure/typed.clj.checker {:mvn/version "1.3.0"}
-                       org.clojure/test.check {:mvn/version "1.1.3"}}
+          :extra-deps {org.typedclojure/typed.clj.checker {:mvn/version "1.3.0"}}
           :jvm-opts ["-Xmx768m"]
           :main-opts ["-m" "com.tailrecursion.check-runner"]}}}
diff --git a/doc/language.md b/doc/language.md
index 91084a0..c2a3331 100644
--- a/doc/language.md
+++ b/doc/language.md
@@ -141,7 +141,7 @@ Unreachable code still receives syntax and JVM compilation checks. Very large re
 
 Mutable locals retain reference identity through assignment and immutable snapshots. Ordinary primitive locals retain Clojure's semantics: separate Object uses may box separately.
 
-Literal pooling is unspecified. Embedded constants, macro-supplied quoted data, regex literals and inlined `^:const` values may share differently from ordinary Clojure. Bind a reference once when its identity matters. Runtime construction sites and cached lookup sites are not duplicated.
+Literal pooling is unspecified. Embedded constants, macro-supplied quoted data, regex literals and inlined `^:const` values may share differently from ordinary Clojure. The concrete classes generated for method values and functional-interface adapters are also unspecified. Bind a reference once when its identity matters; copying that reference preserves identity.
 
 ```clojure
 (let [token (Object.)]
@@ -150,7 +150,7 @@ Literal pooling is unspecified. Embedded constants, macro-supplied quoted data,
     (identical? token y))) ; => true
 ```
 
-Literal keyword calls retain Clojure's cached `IKeywordLookup` behavior, including reentrant target evaluation. An assigned target uses a bundled cache helper; that cache never stores mutable region locals.
+Literal keyword calls use Clojure's cached lookup; indirect keyword calls retain ordinary invocation. Arguments evaluate exactly once before lookup, including when they assign locals or recursively invoke the containing function. Cache layout, installation counts and cache-miss probes are unspecified. Custom `IKeywordLookup` implementations must return equivalent results regardless of cache history; effects or exceptions that depend on that history are outside this contract.
 
 ### Errors
 
diff --git a/src-java/com/tailrecursion/blacktide/runtime/KeywordSite.java b/src-java/com/tailrecursion/blacktide/runtime/KeywordSite.java
deleted file mode 100644
index 189e645..0000000
--- a/src-java/com/tailrecursion/blacktide/runtime/KeywordSite.java
+++ /dev/null
@@ -1,40 +0,0 @@
-package com.tailrecursion.blacktide.runtime;
-
-import clojure.lang.AFn;
-import clojure.lang.ILookupThunk;
-import clojure.lang.IPersistentVector;
-import clojure.lang.IType;
-import clojure.lang.Keyword;
-import clojure.lang.KeywordLookupSite;
-import clojure.lang.PersistentVector;
-import clojure.lang.Symbol;
-
-/** KeywordInvokeExpr's cache, snapshotted before evaluating an effectful target. */
-public final class KeywordSite extends AFn implements IType {
-    public final Keyword keyword;
-    private final KeywordLookupSite site;
-    private ILookupThunk thunk;
-
-    public KeywordSite(Keyword keyword) {
-        this.keyword = keyword;
-        this.site = new KeywordLookupSite(keyword);
-        this.thunk = site;
-    }
-
-    // Clojure emits IType constants through this basis and the public constructor,
-    // giving each emitted call site a cache in both REPL and AOT compilation.
-    public static IPersistentVector getBasis() {
-        return PersistentVector.create((Object) Symbol.intern("keyword"));
-    }
-
-    public ILookupThunk snapshot() { return thunk; }
-
-    @Override public Object invoke(Object saved, Object target) {
-        ILookupThunk before = (ILookupThunk) saved;
-        Object value = before.get(target);
-        if (value != before) return value;
-        ILookupThunk next = site.fault(target);
-        thunk = next;
-        return next.get(target);
-    }
-}
diff --git a/src/com/tailrecursion/blacktide/compiler/jvm.clj b/src/com/tailrecursion/blacktide/compiler/jvm.clj
index ef77400..79e2c7d 100644
--- a/src/com/tailrecursion/blacktide/compiler/jvm.clj
+++ b/src/com/tailrecursion/blacktide/compiler/jvm.clj
@@ -160,7 +160,7 @@
 (np/defpass host-type
   {:from [l/JVM Operation] :to [l/JVM Type] :args [l/JVM-Types]}
   [types operation]
-  :keyword-site com.tailrecursion.blacktide.runtime.KeywordSite
+  :keyword :unknown
   :protocol Object
   :direct (:return operation)
   :instanceof Boolean/TYPE
@@ -189,7 +189,7 @@
   [types operation]
   (let [unchanged {:operation operation :conversions (mapv (fn [_] nil) types)}]
     (case (:op operation)
-      (:adapt :instanceof :method-value :static-field :protocol :keyword-site) unchanged
+      (:adapt :instanceof :method-value :static-field :protocol :keyword) unchanged
       :direct (let [params (:parameters operation)
                     fixed (if (:variadic? operation) (pop params) params)]
                 {:operation operation
diff --git a/src/com/tailrecursion/blacktide/compiler/languages.clj b/src/com/tailrecursion/blacktide/compiler/languages.clj
index 9e46358..ab660d7 100644
--- a/src/com/tailrecursion/blacktide/compiler/languages.clj
+++ b/src/com/tailrecursion/blacktide/compiler/languages.clj
@@ -19,7 +19,7 @@
    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)
+   Operation (t/U (t/HMap :mandatory {:op ':keyword :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)
@@ -185,8 +185,7 @@
                                                    ':zero-guard ':select ':compare ':narrow-int ':narrow-char ':narrow-checked ':widen-char)
                                       :parameters (t/Vec java.lang.Class) :result java.lang.Class
                                       :origins (t/Vec t/Int)} :complete? true)
-             Graph (t/HMap :mandatory {:entry Label :blocks (t/Map Label Block)}
-                           :optional {:slots (t/Vec t/Sym) :live (t/Map Label (t/Set t/Sym))} :complete? true)}})
+             Blocks (t/Map Label Block)}})
 
 ;; Numeric value facts and control plans are independent backend analyses.
 (np/deflanguage Analysis
@@ -210,29 +209,27 @@
 ;; Structured recovery produces Joined expressions. Representation selection
 ;; produces Represented; lexical and numeric lowering follow it.
 ;; Returns carry one public result or the zero/many values of a lexical join.
-(np/deflanguage Structured
+(np/deflanguage Joined
   {Expr (t/U (t/HMap :mandatory {:op ':bind :binding l/Numeric-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 :values (t/Vec 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 ':throw :value l/Machine-Atom} :complete? true)
+             (t/HMap :mandatory {:op ':join :targets (t/Vec t/Sym) :value Expr :body Expr} :complete? true))
+   Build (t/U (t/HMap :mandatory {:op ':built :budget t/Int :expr Expr} :complete? true)
                (t/HMap :mandatory {:op ':exhausted :reason Fallback} :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)
+   Arms (t/U (t/HMap :mandatory {:op ':built :budget t/Int :arms (t/Vec Expr)} :complete? true)
               (t/HMap :mandatory {:op ':exhausted :reason Fallback} :complete? true))
-   Fallback (t/U ':cycles ':validation ':budget ':native-sites)
+   Fallback (t/U ':cycles ':validation ':budget)
    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 :targets (t/Vec t/Sym) :value Expr :body Expr} :complete? true)]}
-   :replace {Context (t/HMap :mandatory {:machine l/Numeric-Program :header (t/Option l/Machine-Label)
-                                        :slots (t/Vec t/Sym) :joins l/Planned-Joins
-                                        :loops l/Planned-LoopBodies :exits l/Planned-Joins
-                                        :active (t/Set l/Machine-Label)
-                                        :stop (t/Option (t/HVec [l/Machine-Label (t/Vec t/Sym)]))} :complete? true)}})
+                 (t/HMap :mandatory {:op ':fallback :reason Fallback} :complete? true))
+   Context (t/HMap :mandatory {:machine l/Numeric-Program :header (t/Option l/Machine-Label)
+                               :slots (t/Vec t/Sym) :joins l/Planned-Joins
+                               :loops l/Planned-LoopBodies :exits l/Planned-Joins
+                               :active (t/Set l/Machine-Label)
+                               :stop (t/Option (t/HVec [l/Machine-Label (t/Vec t/Sym)]))} :complete? true)})
 
 (np/derive-language Represented Joined
   {:replace {Types (t/Map t/Sym java.lang.Class)
diff --git a/src/com/tailrecursion/blacktide/compiler/passes/boundaries.clj b/src/com/tailrecursion/blacktide/compiler/passes/boundaries.clj
index 8257cf4..51c8f44 100644
--- a/src/com/tailrecursion/blacktide/compiler/passes/boundaries.clj
+++ b/src/com/tailrecursion/blacktide/compiler/passes/boundaries.clj
@@ -57,13 +57,6 @@
     [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))))
diff --git a/src/com/tailrecursion/blacktide/compiler/passes/box_origins.clj b/src/com/tailrecursion/blacktide/compiler/passes/box_origins.clj
deleted file mode 100644
index c7a8233..0000000
--- a/src/com/tailrecursion/blacktide/compiler/passes/box_origins.clj
+++ /dev/null
@@ -1,56 +0,0 @@
-(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.passes.value-facts :as facts]
-            [com.tailrecursion.blacktide.compiler.passes.numeric :as numeric]
-            [typed.clojure :as-alias t]))
-
-(np/defpass copy-source {:from [l/Numeric Rhs] :to (t/Option t/Sym) :body true}
-  [rhs]
-  (when (and (= :value (:op rhs)) (= :local (:op (:value rhs)))) (:id (:value rhs))))
-
-(np/defpass origin-values
-  {:from [l/Numeric Rhs] :to (t/Vec l/Machine-Atom) :args [l/Analysis-Types] :body true}
-  [types rhs]
-  (case (:op rhs)
-    :value [(:value rhs)]
-    :numeric (if-let [plan (numeric/plan types rhs)]
-               (numeric/origin-values plan rhs) [])
-    []))
-
-(np/defpass reboxable? {:from [l/Numeric Rhs] :to t/Bool :args [l/Analysis-Types] :body true}
-  [types rhs]
-  (let [origins (origin-values types rhs)]
-    (if (seq origins)
-      ;; Selected references (min/max and NaN division) inherit all possible
-      ;; origins, just like copies. The fixed point checks local dependencies.
-      (every? (fn [^{::t/- l/Machine-Atom} value]
-                (or (= :local (:op value))
-                    (and (= :literal (:op value)) (instance? Long (:value value))
-                         (<= -128 (:value value) 127)))) origins)
-      (case (:op rhs)
-        :numeric (or (when-let [plan (numeric/plan types rhs)] (empty? (:origins plan)))
-                     (class? (facts/rhs-type {} (numeric/source-call rhs))))
-        (:host :convert) (class? (facts/rhs-type {} rhs))
-        false))))
-
-(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
-                  (fn [^{::t/- (t/Set t/Sym)} out ^{::t/- t/Sym} id ^{::t/- (t/Vec l/Numeric-Rhs)} values]
-                    (if (some #(not (reboxable? (:types analysis) %)) values) (conj out id) out))
-                  #{} definitions)
-        denied ((fn ^{::t/- [(t/Set t/Sym) :-> (t/Set t/Sym)]} solve [denied]
-                  (let [next (reduce-kv
-                               (fn [^{::t/- (t/Set t/Sym)} out ^{::t/- t/Sym} id ^{::t/- (t/Vec l/Numeric-Rhs)} values]
-                                 (if (some (fn [rhs]
-                                             (some #(and (= :local (:op %)) (contains? denied (:id %)))
-                                                   (origin-values (:types analysis) rhs))) values) (conj out id) out))
-                               denied definitions)]
-                    (if (= next denied) denied (recur next)))) initial)]
-    (set/difference (set (keys definitions)) denied)))
diff --git a/src/com/tailrecursion/blacktide/compiler/passes/collections.clj b/src/com/tailrecursion/blacktide/compiler/passes/collections.clj
index e21d2d7..5428756 100644
--- a/src/com/tailrecursion/blacktide/compiler/passes/collections.clj
+++ b/src/com/tailrecursion/blacktide/compiler/passes/collections.clj
@@ -24,7 +24,7 @@
     (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.
+      ;; first item or changing its numeric width.
       (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))
diff --git a/src/com/tailrecursion/blacktide/compiler/passes/graph.clj b/src/com/tailrecursion/blacktide/compiler/passes/graph.clj
index c64e4f5..6058c78 100644
--- a/src/com/tailrecursion/blacktide/compiler/passes/graph.clj
+++ b/src/com/tailrecursion/blacktide/compiler/passes/graph.clj
@@ -55,9 +55,9 @@
     {:reads (set/union reads (set/difference (term-reads (:term block)) defs))
      :defs defs :successors (successors (:term block))}))
 
-(np/defpass collect-facts {:from [l/Numeric Graph] :to [l/CFG Facts] :body true}
-  [program]
-  (update-vals (:blocks program) block-facts))
+(np/defpass collect-facts {:from [l/Numeric Blocks] :to [l/CFG Facts] :body true}
+  [blocks]
+  (update-vals blocks block-facts))
 
 (np/defpass fact-at {:from [l/CFG Label] :to [l/CFG BlockFacts] :args [l/CFG-Facts] :body true}
   [facts id]
@@ -129,12 +129,12 @@
 (np/defpass allocate
   {:from [l/CFG Program] :to [l/Machine Program] :body true}
   [program]
-  (let [live (liveness (collect-facts program))]
+  (let [live (liveness (collect-facts (:blocks program)))]
     (assoc program :live live :slots (vec (sort (reduce set/union #{} (vals live)))))))
 
 (np/defpass validate {:from [l/CFG Program] :to [l/CFG Program] :body true}
   [program]
-  (let [facts (collect-facts program)
+  (let [facts (collect-facts (:blocks program))
         program (validate-edges facts program)
         seen (reachable facts (:entry program))
         reachable-facts (select-keys facts seen)]
diff --git a/src/com/tailrecursion/blacktide/compiler/passes/host_emit.clj b/src/com/tailrecursion/blacktide/compiler/passes/host_emit.clj
index 178c380..c276041 100644
--- a/src/com/tailrecursion/blacktide/compiler/passes/host_emit.clj
+++ b/src/com/tailrecursion/blacktide/compiler/passes/host_emit.clj
@@ -3,7 +3,6 @@
   explicitly; Object prevents boxed storage from inventing a more specific type."
   (:require [com.tailrecursion.nanopass :as np]
             [com.tailrecursion.blacktide.compiler.languages :as l]
-            [com.tailrecursion.blacktide.runtime]
             [typed.clojure :as-alias t]))
 
 (np/defpass global-form {:from [l/Machine Name] :to [l/Machine Code] :body true}
@@ -51,7 +50,7 @@
 (np/defpass host-form
   {: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))
+  :keyword (list (:key operation) (first args))
   :protocol
   (let [locals (mapv (fn [_] (gensym "protocol__")) args)
         receiver (first locals) function (second locals) arguments (subvec locals 2)
diff --git a/src/com/tailrecursion/blacktide/compiler/passes/joins.clj b/src/com/tailrecursion/blacktide/compiler/passes/joins.clj
index 9f9c7d0..5d5eab2 100644
--- a/src/com/tailrecursion/blacktide/compiler/passes/joins.clj
+++ b/src/com/tailrecursion/blacktide/compiler/passes/joins.clj
@@ -91,7 +91,7 @@
 
 (np/defpass plan {:from [l/Numeric Program] :to [l/Planned Program] :body true}
   [machine]
-  (let [facts (graph/collect-facts machine)
+  (let [facts (graph/collect-facts (:blocks machine))
         known (postdominators facts)
         bodies (loops/analyze (:entry machine) facts)]
     {:machine machine
diff --git a/src/com/tailrecursion/blacktide/compiler/passes/native.clj b/src/com/tailrecursion/blacktide/compiler/passes/native.clj
index b23496f..38989a7 100644
--- a/src/com/tailrecursion/blacktide/compiler/passes/native.clj
+++ b/src/com/tailrecursion/blacktide/compiler/passes/native.clj
@@ -42,12 +42,8 @@
                (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)))))
+    :invoke (hinted-form (:hint (:jvm expr))
+                        (list* (form bound (:function node)) (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))
diff --git a/src/com/tailrecursion/blacktide/compiler/passes/source.clj b/src/com/tailrecursion/blacktide/compiler/passes/source.clj
index 874510a..2105890 100644
--- a/src/com/tailrecursion/blacktide/compiler/passes/source.clj
+++ b/src/com/tailrecursion/blacktide/compiler/passes/source.clj
@@ -9,7 +9,6 @@
             [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
@@ -107,21 +106,6 @@
     [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]
@@ -277,9 +261,9 @@
         (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))
+          (and (= :literal (:op fnode)) (keyword? key) (= 1 arity))
+          [supply (expression ctx {:hint hint :var-meta {}}
+                              {:op :host :operation {:op :keyword :key key} :args args})]
           :else [supply (expression ctx {:hint hint :var-meta {}} node)]))))
 
   (parse-host
diff --git a/src/com/tailrecursion/blacktide/compiler/passes/storage.clj b/src/com/tailrecursion/blacktide/compiler/passes/storage.clj
index 78d58c3..50cf93f 100644
--- a/src/com/tailrecursion/blacktide/compiler/passes/storage.clj
+++ b/src/com/tailrecursion/blacktide/compiler/passes/storage.clj
@@ -1,8 +1,8 @@
 (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;
-  reboxable results may be boxed at return when no other observer requires a box."
+  specialize only to long/double. Copies and numeric plans propagate boxing
+  requirements; the public return boundary boxes the selected representation."
   (:require [clojure.set :as set]
             [com.tailrecursion.nanopass :as np]
             [com.tailrecursion.blacktide.compiler.languages :as l]
@@ -10,7 +10,6 @@
             [com.tailrecursion.blacktide.compiler.passes.native :as native]
             [com.tailrecursion.blacktide.compiler.passes.numeric :as numeric]
             [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 [l/Joined Expr] :to (t/Set t/Sym)}
@@ -33,14 +32,17 @@
   [types observed operation]
   (let [rhs (:value operation)]
     (case (:op rhs)
-      :value (if-not (contains? observed (:target operation)) #{}
-               (if (contains? types (:target operation))
-                 (if-let [source (origins/copy-source rhs)]
-                   (if (contains? types source) #{} #{(:target operation)}) #{})
-                 (graph/atom-reads (:value rhs))))
+      :value (let [value (:value rhs) target (:target operation)]
+               (cond
+                 (not (contains? observed target)) #{}
+                 (not (contains? types target)) (graph/atom-reads value)
+                 (and (= :local (:op value)) (not (contains? types (:id value)))) #{target}
+                 :else #{}))
       :numeric (if-let [plan (numeric/plan types rhs)]
-                 (into (if (contains? types (:target operation)) #{}
-                         (into #{} (mapcat graph/atom-reads) (numeric/origin-values plan rhs)))
+                 (into (if (= (get types (:target operation)) (:result plan)) #{}
+                         ;; Boxing an operand can change the selected overload's
+                         ;; result representation. Its target must follow suit.
+                         (into #{(:target operation)} (mapcat graph/atom-reads) (numeric/origin-values plan rhs)))
                        (mapcat (fn [arg ^Class parameter]
                                  (if (.isPrimitive parameter) #{} (graph/atom-reads (:value arg))))
                                (:args rhs) (:parameters plan)))
@@ -69,11 +71,10 @@
          (if (= observed next) observed (recur next)))) initial)))
 
 (np/defpass term-boxes
-  {:from [l/Machine Term] :to (t/Set t/Sym) :args [l/Represented-Types (t/Set t/Sym)] :body true}
-  [types reboxable term]
+  {:from [l/Machine Term] :to (t/Set t/Sym) :args [l/Represented-Types] :body true}
+  [types term]
   (case (:op term)
-    :return (if (and (= :local (:op (:value term))) (contains? reboxable (:id (:value term))))
-              #{} (graph/term-reads term))
+    :return #{}
     :branch (if (= Boolean/TYPE (facts/atom-type types (:test term))) #{} (graph/term-reads term))
     :switch (if (= Long/TYPE (facts/atom-type types (:test term))) #{} (graph/term-reads term))
     (graph/term-reads term)))
@@ -82,8 +83,7 @@
   {:from [l/Joined Expr] :to [l/Represented Program] :args [l/Analysis-Program] :body true}
   [analysis expr]
   ;; Emission preserves the public Object boundary at every exit.
-  (let [reboxable (origins/reboxable-values analysis)
-        slots (loop-slots expr)
+  (let [slots (loop-slots expr)
         initial (reduce-kv
                   (fn [^{::t/- l/Represented-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)))
@@ -93,7 +93,7 @@
         blocks (vals (:blocks machine))
         types ((fn ^{::t/- [l/Represented-Types :-> l/Represented-Types]} solve [types]
                  (let [boxed (into (into #{} (mapcat #(operation-boxes types observed %)) (mapcat :ops blocks))
-                                   (mapcat #(term-boxes types reboxable (:term %))) blocks)
+                                   (mapcat #(term-boxes types (:term %))) blocks)
                        next (apply dissoc types boxed)]
                    (if (= types next) types (recur next)))) initial)]
     {:body expr :types types}))
diff --git a/src/com/tailrecursion/blacktide/compiler/passes/structure.clj b/src/com/tailrecursion/blacktide/compiler/passes/structure.clj
index ff5a566..c95c706 100644
--- a/src/com/tailrecursion/blacktide/compiler/passes/structure.clj
+++ b/src/com/tailrecursion/blacktide/compiler/passes/structure.clj
@@ -1,7 +1,7 @@
 (ns ^:typed.clojure com.tailrecursion.blacktide.compiler.passes.structure
   "Recover lexical control from reducible loop regions. Nested loops yield
   their live results; tail loops keep their continuation without a packet.
-  Bounded duplication preserves native sites and dead-code validation."
+  Recovery bounds code duplication and preserves dead-code validation."
   (:require [com.tailrecursion.nanopass :as np]
             [com.tailrecursion.blacktide.compiler.languages :as l]
             [com.tailrecursion.blacktide.compiler.passes.graph :as graph]
@@ -45,11 +45,11 @@
     {:from [l/Machine Term] :to [l/Joined Arms] :args [l/Joined-Context t/Int] :body true}
     [ctx 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}
+      ((fn ^{::t/- [t/Int t/Int (t/Vec l/Joined-Expr) :-> l/Joined-Arms]} build [index budget out]
+         (if (= index (count (:targets term))) {:op :built :budget budget :arms out}
            (let [result (edge ctx 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 [] [])
+               (recur (inc index) (:budget result) (conj out (:expr result))) result)))) 0 budget [])
       {:op :exhausted :reason :budget}))
 
   (terminator
@@ -57,16 +57,16 @@
     [ctx budget term]
     :jump (edge ctx budget (:target term))
     :return (if (:stop ctx) {:op :exhausted :reason :cycles}
-                {:op :built :budget budget :blocks [] :expr {:op :return :values [(:value term)]}})
-    :throw {:op :built :budget budget :blocks [] :expr term}
+                {:op :built :budget budget :expr {:op :return :values [(:value term)]}})
+    :throw {:op :built :budget budget :expr term}
     :switch (let [result (arms ctx budget term)]
               (if (= :built (:op result))
-                {:op :built :budget (:budget result) :blocks (:blocks result) :expr {:op :switch :test (:test term) :arms (:arms result)}} result))
+                {:op :built :budget (:budget result) :expr {:op :switch :test (:test term) :arms (:arms result)}} result))
     :branch (let [yes (edge ctx budget (:yes term))]
               (if (= :exhausted (:op yes)) yes
                 (let [no (edge ctx (:budget yes) (:no term))]
                   (if (= :exhausted (:op no)) no
-                    {:op :built :budget (:budget no) :blocks (into (:blocks yes) (:blocks no))
+                    {:op :built :budget (:budget no)
                      :expr {:op :branch :test (:test term) :yes (:expr yes) :no (:expr no)}})))))
 
   (joined
@@ -77,8 +77,7 @@
       (if (= :built (:op result))
         (let [tail (edge ctx (:budget result) (:target join))]
           (if (= :built (:op tail))
-            (assoc tail :blocks (into (:blocks result) (:blocks tail))
-                        :expr {:op :join :targets (:results join) :value (:expr result) :body (:expr tail)}) tail))
+            (assoc tail :expr {:op :join :targets (:results join) :value (:expr result) :body (:expr tail)}) tail))
         result)))
 
   (body
@@ -91,7 +90,7 @@
               result (if (and join (within-stop? ctx join))
                        (joined ctx budget (:term block) join)
                        (terminator ctx budget (:term block)))]
-          (if (= :built (:op result)) (assoc result :blocks (conj (:blocks result) id) :expr (wrap (:expr result) block)) result)))))
+          (if (= :built (:op result)) (assoc result :expr (wrap (:expr result) block)) result)))))
 
   (loop-form
     {:from [l/Machine Label] :to [l/Joined Build] :args [l/Joined-Context t/Int] :body true}
@@ -108,10 +107,10 @@
     (let [stop (:stop ctx)]
       (cond
         (and stop (= id (first stop)))
-        {:op :built :budget budget :blocks []
+        {:op :built :budget budget
          :expr {:op :return :values (mapv (fn [id] {:op :local :id id}) (second stop))}}
         (= id (:header ctx))
-        (if (pos? budget) {:op :built :budget (dec budget) :blocks [] :expr {:op :recur :slots (:slots ctx)}}
+        (if (pos? budget) {:op :built :budget (dec budget) :expr {:op :recur :slots (:slots ctx)}}
             {:op :exhausted :reason :budget})
         (contains? (:active ctx) id) {:op :exhausted :reason :cycles}
         (contains? (:loops ctx) id)
@@ -122,45 +121,16 @@
                 (if (= :built (:op result))
                   (let [tail (edge ctx (:budget result) (:target join))]
                     (if (= :built (:op tail))
-                      (assoc tail :blocks (into (:blocks result) (:blocks tail))
-                                  :expr {:op :join :targets (:results join) :value (:expr result) :body (:expr tail)}) tail)) result))
+                      (assoc tail :expr {:op :join :targets (:results join) :value (:expr result) :body (:expr tail)}) tail)) result))
               {:op :exhausted :reason :cycles})
             {:op :exhausted :reason :cycles})
           (loop-form ctx budget id))
         :else (body ctx budget id)))))
 
-(np/defpass construction-site?
-  {:from [l/Numeric 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/Numeric 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}
   [plan]
   (let [machine (:machine plan)
-        reachable (graph/reachable (graph/collect-facts machine) (:entry machine))
+        reachable (graph/reachable (graph/collect-facts (:blocks machine)) (:entry machine))
         dead (apply dissoc (:blocks machine) reachable)
         loops (:loops plan)]
     (cond
@@ -172,6 +142,5 @@
             budget (* 4 (reduce + 0 (map #(inc (count (:ops %))) (vals (select-keys (:blocks machine) reachable)))))
             result (edge ctx 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 :structured :body (:expr result)}
           {:op :fallback :reason (:reason result)})))))
diff --git a/src/com/tailrecursion/blacktide/compiler/passes/value_facts.clj b/src/com/tailrecursion/blacktide/compiler/passes/value_facts.clj
index 297acfa..31d61ff 100644
--- a/src/com/tailrecursion/blacktide/compiler/passes/value_facts.clj
+++ b/src/com/tailrecursion/blacktide/compiler/passes/value_facts.clj
@@ -20,7 +20,7 @@
 (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.
+  ;; Quoted Integer constants stay boxed to preserve their width.
   :literal (cond (instance? Long (:value node)) (primitive Long/TYPE)
                  (instance? Double (:value node)) (primitive Double/TYPE)
                  :else :boxed))
@@ -67,5 +67,5 @@
 
 (np/defpass analyze {:from [l/Numeric Program] :to [l/Analysis Program] :body true}
   [machine]
-  (let [reachable (graph/reachable (graph/collect-facts {:entry (:entry machine) :blocks (:blocks machine)}) (:entry machine))]
+  (let [reachable (graph/reachable (graph/collect-facts (:blocks machine)) (:entry machine))]
     {:machine machine :reachable reachable :types (solve (definitions reachable machine))}))
diff --git a/src/com/tailrecursion/blacktide/runtime.clj b/src/com/tailrecursion/blacktide/runtime.clj
deleted file mode 100644
index 9e6c76b..0000000
--- a/src/com/tailrecursion/blacktide/runtime.clj
+++ /dev/null
@@ -1,7 +0,0 @@
-(ns com.tailrecursion.blacktide.runtime
-  "Internal emission support. Cache literals are created during final Clojure
-  analysis, so macros duplicating an expansion still get distinct call sites."
-  (:import [com.tailrecursion.blacktide.runtime KeywordSite]))
-
-(defmacro keyword-site [keyword]
-  (list 'quote (KeywordSite. keyword)))
diff --git a/test/com/tailrecursion/assignment_model.clj b/test/com/tailrecursion/assignment_model.clj
deleted file mode 100644
index 54b1be9..0000000
--- a/test/com/tailrecursion/assignment_model.clj
+++ /dev/null
@@ -1,76 +0,0 @@
-(ns com.tailrecursion.assignment-model
-  "Independent assignment semantics, event traces, and bounded source generators.
-  Shared by the nanopass client tests and the real Loophole compiler tests."
-  (:require [clojure.test.check.generators :as gen]))
-
-(defn lit [value] {:op :const :value value})
-(defn read-var [name] {:op :read :binding name})
-(defn batch [op & writes] {:op op :writes (mapv vec (partition 2 writes))})
-(defn sequence-of [& forms] {:op :do :forms (vec forms)})
-(defn event [tag value] {:op :event :tag tag :value value})
-
-(defn evaluate [form state]
-  (let [run #(evaluate % %2)
-        binding (:binding form)]
-    (case (:op form)
-      :const [(:value form) state]
-      :read [(get-in state [:vars binding]) state]
-      :temp (let [id (:id form)]
-              (when-not (contains? (:temps state) id)
-                (throw (ex-info "Unbound temporary" {:id id})))
-              [(get-in state [:temps id]) state])
-      :add (let [[a state] (run (:left form) state)
-                 [b state] (run (:right form) state)] [(+ a b) state])
-      :do (reduce (fn [[_ state] form] (run form state)) [nil state] (:forms form))
-      :if (let [[test state] (run (:test form) state)]
-            (run (if test (:then form) (:else form)) state))
-      :event (let [[value state] (run (:value form) state)]
-               [value (update state :trace conj [(:tag form) value])])
-      :throw (throw (ex-info "Source exception" {:status :throw :tag (:tag form) :state state}))
-      ;; Source assignments define their semantics directly, independent of
-      ;; the pass's generated binding tree and the output's write primitive.
-      :setq (reduce (fn [[_ state] [name rhs]]
-                      (let [[value state] (run rhs state)]
-                        [value (assoc-in state [:vars name] value)]))
-                    [nil state] (:writes form))
-      :psetq (let [[values state]
-                   (reduce (fn [[values state] [_ rhs]]
-                             (let [[value state] (run rhs state)]
-                               [(conj values value) state]))
-                           [[] state] (:writes form))]
-               [nil (update state :vars merge (zipmap (map first (:writes form)) values))])
-      ;; The output interpreter knows only a single write and lexical temps.
-      :write (let [[value state] (run (:value form) state)]
-               [value (assoc-in state [:vars binding] value)])
-      :bind (let [[value state] (run (:value form) state)
-                  temps (:temps state)
-                  [result state] (run (:body form) (assoc-in state [:temps (:id form)] value))]
-              [result (assoc state :temps temps)]))))
-
-(defn execute [form vars]
-  (let [state {:vars vars :temps {} :trace []}]
-    (try
-      (let [[value state] (evaluate form state)]
-        (assoc (select-keys state [:vars :trace]) :value value :status :return))
-      (catch clojure.lang.ExceptionInfo e
-        (let [{:keys [state status tag]} (ex-data e)]
-          (if status
-            (cond-> (assoc (select-keys state [:vars :trace]) :status status)
-              tag (assoc :tag tag))
-            (throw e)))))))
-
-(defn expression [depth]
-  (let [leaf (gen/one-of [(gen/fmap lit (gen/choose -5 5))
-                          (gen/fmap read-var (gen/elements '[x y]))])]
-    (if (zero? depth) leaf
-      (let [child (expression (dec depth))
-            writes (gen/vector (gen/tuple (gen/elements '[x y]) child) 0 3)]
-        (gen/one-of
-          [leaf
-           (gen/let [a child b child] {:op :add :left a :right b})
-           (gen/let [a child b child] (sequence-of a b))
-           (gen/let [test gen/boolean then child else child]
-             {:op :if :test (lit test) :then then :else else})
-           (gen/let [tag (gen/elements [:a :b :c]) value child] (event tag value))
-           (gen/let [writes writes] (sequence-of {:op :setq :writes writes} (read-var 'x)))
-           (gen/let [writes writes] (sequence-of {:op :psetq :writes writes} (read-var 'y)))])))))
diff --git a/test/com/tailrecursion/blacktide/aot_test.clj b/test/com/tailrecursion/blacktide/aot_test.clj
index eac31f7..a7b77f5 100644
--- a/test/com/tailrecursion/blacktide/aot_test.clj
+++ b/test/com/tailrecursion/blacktide/aot_test.clj
@@ -21,11 +21,6 @@
     (try
       (is (zero? (:exit compiled)) (:output compiled))
       (when (zero? (:exit compiled))
-        ;; A library JAR includes this Java class; AOT does not copy dependencies.
-        (let [path "com/tailrecursion/blacktide/runtime/KeywordSite.class"
-              target (io/file directory path)]
-          (io/make-parents target)
-          (with-open [source (io/input-stream (io/resource path))] (io/copy source target)))
         (let [loaded (subprocess (str/join java.io.File/pathSeparator (cons (.getPath directory) dependencies))
                                  '(do (require 'com.tailrecursion.blacktide.aot-fixture)
                                       (assert (= 9 (com.tailrecursion.blacktide.aot-fixture/sample 4)))
@@ -39,4 +34,18 @@
                                                             (:k (com.tailrecursion.blacktide/setq x {:k 11}))))))
                                       (println "AOT client and source-free macro expansion passed")))]
           (is (zero? (:exit loaded)) (:output loaded))))
+      ;; Execute generated function classes with only Clojure and the client.
+      ;; Loading the client's namespace would also execute its macro require.
+      (when (zero? (:exit compiled))
+        (doseq [file (file-seq directory)
+                :when (and (.isFile file) (not (.startsWith (.getName file) "aot_fixture")))]
+          (io/delete-file file))
+        (let [loaded (subprocess (str/join java.io.File/pathSeparator (cons (.getPath directory) dependencies))
+                       '(do
+                          (assert (= 9 (clojure.lang.Reflector/invokeStaticMethod
+                                         "com.tailrecursion.blacktide.aot_fixture$sample" "invokeStatic" (to-array [4]))))
+                          (assert (= 7 (clojure.lang.Reflector/invokeStaticMethod
+                                         "com.tailrecursion.blacktide.aot_fixture$keyword_read" "invokeStatic" (to-array [{:k 7}]))))
+                          (println "Generated code runs without compiler classes")))]
+          (is (zero? (:exit loaded)) (:output loaded))))
       (finally (doseq [file (reverse (file-seq directory))] (io/delete-file file))))))
diff --git a/test/com/tailrecursion/blacktide/assignment_model.clj b/test/com/tailrecursion/blacktide/assignment_model.clj
new file mode 100644
index 0000000..468c5a9
--- /dev/null
+++ b/test/com/tailrecursion/blacktide/assignment_model.clj
@@ -0,0 +1,53 @@
+(ns com.tailrecursion.blacktide.assignment-model
+  "Independent assignment semantics, event traces, and bounded source generators.
+  The interpreter models source assignments without using compiler lowering."
+  (:require [clojure.test.check.generators :as gen]))
+
+(defn lit [value] {:op :const :value value})
+(defn read-var [name] {:op :read :binding name})
+(defn batch [op & writes] {:op op :writes (mapv vec (partition 2 writes))})
+(defn sequence-of [& forms] {:op :do :forms (vec forms)})
+(defn event [tag value] {:op :event :tag tag :value value})
+
+(defn evaluate [form state]
+  (case (:op form)
+    :const [(:value form) state]
+    :read [(get-in state [:vars (:binding form)]) state]
+    :add (let [[a state] (evaluate (:left form) state)
+               [b state] (evaluate (:right form) state)] [(+ a b) state])
+    :do (reduce (fn [[_ state] form] (evaluate form state)) [nil state] (:forms form))
+    :if (let [[test state] (evaluate (:test form) state)]
+          (evaluate (if test (:then form) (:else form)) state))
+    :event (let [[value state] (evaluate (:value form) state)]
+             [value (update state :trace conj [(:tag form) value])])
+    :setq (reduce (fn [[_ state] [name rhs]]
+                    (let [[value state] (evaluate rhs state)]
+                      [value (assoc-in state [:vars name] value)]))
+                  [nil state] (:writes form))
+    ;; Snapshot every parallel RHS before applying any pending write.
+    :psetq (let [[values state]
+                 (reduce (fn [[values state] [_ rhs]]
+                           (let [[value state] (evaluate rhs state)]
+                             [(conj values value) state]))
+                         [[] state] (:writes form))]
+             [nil (update state :vars merge (zipmap (map first (:writes form)) values))])))
+
+(defn execute [form vars]
+  (let [[value state] (evaluate form {:vars vars :trace []})]
+    (assoc state :value value)))
+
+(defn expression [depth]
+  (let [leaf (gen/one-of [(gen/fmap lit (gen/choose -5 5))
+                          (gen/fmap read-var (gen/elements '[x y]))])]
+    (if (zero? depth) leaf
+      (let [child (expression (dec depth))
+            writes (gen/vector (gen/tuple (gen/elements '[x y]) child) 0 3)]
+        (gen/one-of
+          [leaf
+           (gen/let [a child b child] {:op :add :left a :right b})
+           (gen/let [a child b child] (sequence-of a b))
+           (gen/let [test gen/boolean then child else child]
+             {:op :if :test (lit test) :then then :else else})
+           (gen/let [tag (gen/elements [:a :b :c]) value child] (event tag value))
+           (gen/let [writes writes] (sequence-of {:op :setq :writes writes} (read-var 'x)))
+           (gen/let [writes writes] (sequence-of {:op :psetq :writes writes} (read-var 'y)))])))))
diff --git a/test/com/tailrecursion/blacktide/assignment_test.clj b/test/com/tailrecursion/blacktide/assignment_test.clj
index a235757..f488f69 100644
--- a/test/com/tailrecursion/blacktide/assignment_test.clj
+++ b/test/com/tailrecursion/blacktide/assignment_test.clj
@@ -7,7 +7,7 @@
             [clojure.test.check.properties :as prop]
             [com.tailrecursion.blacktide :as lh :refer [loophole setq psetq]]
             [com.tailrecursion.blacktide.backend-fixture :as backend]
-            [com.tailrecursion.assignment-model :as oracle]))
+            [com.tailrecursion.blacktide.assignment-model :as oracle]))
 
 (defn add [a b] (+ a b))
 (defn event! [events tag value] (swap! events conj [tag value]) value)
@@ -83,11 +83,11 @@
            (list 'let ['events '(atom [])]
                  (list 'loophole '[x (long x) y (long y)]
                        (list 'let ['result (render node)]
-                             {:status :return :value 'result :trace '(deref events)
+                             {:value 'result :trace '(deref events)
                               :vars {(list 'quote 'x) 'x (list 'quote 'y) 'y}})))) :default) nil))
 
 (deftest generated-assignment-programs
-  ;; Reuse only the independent source interpreter, not its lowering algorithm.
+  ;; Compare generated source with the independent interpreter.
   (let [property (prop/for-all [node (oracle/expression 3)]
                    (let [compiled (compile-node node)]
                      (every? true?
diff --git a/test/com/tailrecursion/blacktide/backend_test.clj b/test/com/tailrecursion/blacktide/backend_test.clj
index a430055..35d6f31 100644
--- a/test/com/tailrecursion/blacktide/backend_test.clj
+++ b/test/com/tailrecursion/blacktide/backend_test.clj
@@ -8,7 +8,6 @@
             [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.jvm :as jvm]
             [com.tailrecursion.blacktide.compiler.passes.prepare :as prepare]
             [com.tailrecursion.blacktide.compiler.passes.lower :as lower]
@@ -85,7 +84,7 @@
 
 (deftest integer-conversion-overloads-are-not-interchangeable
   (let [outcome (fn [f x] (try (let [v (f x)] [v (class v)])
-                             (catch Throwable e [(class e) (ex-message e)])))
+                             (catch Throwable e [(class e)])))
         reference (fn [x] (int (identity x)))]
     (doseq [[cast values]
             [['long [Long/MIN_VALUE (dec (long Integer/MIN_VALUE)) Integer/MIN_VALUE
@@ -105,21 +104,18 @@
                        :seed 173205)]
           (is (:pass? result) (pr-str [mode result])))))))
 
-(deftest cached-long-origins-retain-value-class-and-identity
-  ;; Compiler.NumberExpr initializes constants with Long.valueOf, including
-  ;; distinct Long objects supplied by macros. Only its guaranteed cache is safe.
-  (doseq [n [-129 -128 -1 0 1 127 128 1000]]
-    (let [literal (Long. (long n)) cached? (<= -128 n 127)
-          form (list 'loophole ['i 0 'x literal]
+(deftest return-boxing-preserves-values-and-observed-references
+  (doseq [literal [-129 0 1000 -0.0 1.5]]
+    (let [form (list 'loophole ['i 0 'x literal]
                      '(while (< i 2) (setq i (inc i))) 'x)]
-      (is (= cached? (origins/reboxable? {} {:op :value :value (m/literal literal)})))
+      (is (some (fn [[id type]] (and (.endsWith (name id) "__x")
+                                     (= type (if (instance? Long literal) Long/TYPE Double/TYPE))))
+                (:types (capture #'storage/select-storage form))))
       (doseq [mode [:default :dispatcher]]
-        (let [f (compile-program form mode) first-value (f nil)]
-          (is (= n first-value))
-          (is (= Long (class first-value)))
-          (is (identical? first-value (f nil)))
-          (is (= cached? (identical? (Long/valueOf (long n)) first-value)))))))
-  ;; Small external boxes and constructor results are not cached literals.
+        (let [value ((compile-program form mode) nil)]
+          (is (= (Double/doubleToRawLongBits (double literal)) (Double/doubleToRawLongBits (double value))))
+          (is (= (class literal) (class value)))))))
+  ;; Externally supplied and observed boxes retain identity regardless of value.
   (doseq [mode [:default :dispatcher]
           value [(Long. 0) (Long. 127) (Long. 128) (Integer. 0) (Double. 0.0)]]
     (let [f (compile-program '(loophole [i 0 x events]
diff --git a/test/com/tailrecursion/blacktide/generated_test.clj b/test/com/tailrecursion/blacktide/generated_test.clj
index 994c284..8ad379a 100644
--- a/test/com/tailrecursion/blacktide/generated_test.clj
+++ b/test/com/tailrecursion/blacktide/generated_test.clj
@@ -15,7 +15,7 @@
 
 (defn three-steps [x k] (+ x (* 3 k)))
 
-(def operations [:parallel :if :case :snapshot :arguments :shadow :helper :loop :check :truth :cast])
+(def operations [:parallel :if :case :snapshot :arguments :shadow :helper :loop :check :truth :cast :lookup])
 
 (defn model-step [{:keys [x y] :as state} i index [op k]]
   (if (and (= op :check) (= i k)) (reduced (assoc state :throw ArithmeticException))
@@ -30,6 +30,7 @@
                   :loop [(+ x (* 2 k)) y]
                   :truth [(+ x (if (even? k) k (- k))) y]
                   :cast [(+ x k) y]
+                  :lookup [(+ x k) (+ x y)]
                   :check [x y])]
       (assoc state :x x :y y :trace (conj (:trace state) [i index x y])))))
 
@@ -63,6 +64,9 @@
         :truth `(setq ~'x (if ~(if (even? k) (if (pos? k) 0 :selected) (if (pos? k) false nil))
                            (+ ~'x ~k) (- ~'x ~k)))
         :cast `(setq ~'x (+ (long ~'x) ~k))
+        :lookup `(setq ~'y (:value (case (mod ~'i 3)
+                                    0 (let [~'old ~'x] (setq ~'x (+ ~'x ~k)) {:value (+ ~'old ~'y)})
+                                    (do (setq ~'x (+ ~'x ~k)) {:value (+ (- ~'x ~k) ~'y)}))))
         :check `(when (= ~'i ~k) (+ Long/MAX_VALUE 1)))
      (swap! ~'events conj [~(observed numeric? 'i) ~index ~(observed numeric? 'x) ~(observed numeric? 'y)])))
 
diff --git a/test/com/tailrecursion/blacktide/interop_values_test.clj b/test/com/tailrecursion/blacktide/interop_values_test.clj
new file mode 100644
index 0000000..fbe97e0
--- /dev/null
+++ b/test/com/tailrecursion/blacktide/interop_values_test.clj
@@ -0,0 +1,50 @@
+(ns com.tailrecursion.blacktide.interop-values-test
+  "Method values and adapters retain their behavior across recovered control.
+  Bound references preserve identity; generated wrapper classes may differ."
+  (:require [clojure.test :refer [deftest is]]
+            [com.tailrecursion.blacktide.compiler.passes.structure :as structure]
+            [com.tailrecursion.blacktide.backend-fixture :as backend]
+            [com.tailrecursion.blacktide.jvm-fixture]))
+
+(def flags [true false false true])
+
+(deftest java-function-values-across-joins
+  (let [target (proxy [java.util.HashMap] [] (compute [_ callback] callback))
+        callback (fn [x y] [x y])]
+    (doseq [[tail invoke]
+            [['String/.length #(% "abc")]
+             [(with-meta 'String/valueOf {:param-tags ['Object]}) #(% "abc")]
+             [(with-meta 'String/new {:param-tags ['String]}) #(% "abc")]
+             ['(let [^java.util.function.BiFunction f callback] f)
+              #(.apply ^java.util.function.BiFunction % :left :right)]
+             ['(java.util.Map/.compute target :key callback)
+              #(.apply ^java.util.function.BiFunction % :left :right)]
+             ['(com.tailrecursion.blacktide.jvm-fixture/map-compute target :key callback)
+              #(.apply ^java.util.function.BiFunction % :left :right)]]
+            mode [:default :dispatcher]]
+      (let [ordinary (list 'let ['values '(if flag [1 2] [3 4])] (list 'vector 'values tail))
+            compiled (list 'loophole '[a 0 b 0]
+                           '(if flag (setq a 1 b 2) (setq a 3 b 4)) (list 'vector '[a b] tail))
+            observe (fn [body mode]
+                      (let [f ((backend/compile-program (list 'fn '[flag target callback] body) mode) nil)
+                            results (mapv #(f % target callback) flags)]
+                        (mapv (fn [[pair f]] [pair (invoke f)]) results)))]
+        (is (= :structured (:op (backend/capture #'structure/recover
+                                 (list 'fn '[flag target callback] compiled)))))
+        (is (= (observe ordinary :ordinary) (observe compiled mode)) (pr-str [mode tail]))))))
+
+(deftest bound-adapters-retain-identity-and-effects
+  (doseq [mode [:default :dispatcher] flag [false true]]
+    (let [events (atom []) callback (fn [x y] (swap! events conj [x y]) (+ x y))
+          f ((backend/compile-program
+               '(fn [flag callback]
+                  (loophole [a 0 b 0 saved nil]
+                    (if flag (setq a 1 b 2) (setq a 3 b 4))
+                    (let [^java.util.function.BiFunction f (do (swap! events conj :adapt) callback)]
+                      (setq saved f)
+                      [(identical? saved f) (.apply f a b) saved]))) mode) events)
+          [same? result adapter] (f flag callback)]
+      (is same?)
+      (is (= (if flag 3 7) result))
+      (is (= 9 (.apply ^java.util.function.BiFunction adapter 4 5)))
+      (is (= [:adapt (if flag [1 2] [3 4]) [4 5]] @events)))))
diff --git a/test/com/tailrecursion/blacktide/jvm_fixture.clj b/test/com/tailrecursion/blacktide/jvm_fixture.clj
index 7db9df1..cd7ef1d 100644
--- a/test/com/tailrecursion/blacktide/jvm_fixture.clj
+++ b/test/com/tailrecursion/blacktide/jvm_fixture.clj
@@ -1,5 +1,5 @@
 (ns com.tailrecursion.blacktide.jvm-fixture
-  "Shared JVM dispatch and lookup fixtures, with a native Clojure outcome oracle."
+  "Shared JVM dispatch fixtures, with a native Clojure outcome oracle."
   (:require [clojure.java.io :as io]
             [com.tailrecursion.blacktide :refer [loophole setq psetq label goto]]
             [com.tailrecursion.blacktide.backend-fixture :as backend]))
@@ -38,20 +38,6 @@
     (catch Throwable error
       {:error (class (backend/root-error error))})))
 
-(deftype ObservedLookup [events entries]
-  clojure.lang.ILookup
-  (valAt [_ key] (swap! events conj [:val key]) (get entries key))
-  (valAt [_ key default] (swap! events conj [:default key]) (get entries key default))
-  clojure.lang.IKeywordLookup
-  (getLookupThunk [_ key]
-    (swap! events conj [:install key])
-    (reify clojure.lang.ILookupThunk
-      (get [thunk target]
-        (if (instance? ObservedLookup target)
-          (do (swap! (.-events ^ObservedLookup target) conj [:get key])
-              (get (.-entries ^ObservedLookup target) key))
-          thunk)))))
-
 (def java-map {:on 'java.util.Map :method-map {:map-compute :compute}})
 (defn map-compute [_ k f] (f k :fallback))
 (alter-meta! #'map-compute assoc :protocol #'java-map)
diff --git a/test/com/tailrecursion/blacktide/lookup_test.clj b/test/com/tailrecursion/blacktide/lookup_test.clj
index 67dc025..4f9627a 100644
--- a/test/com/tailrecursion/blacktide/lookup_test.clj
+++ b/test/com/tailrecursion/blacktide/lookup_test.clj
@@ -1,17 +1,29 @@
 (ns com.tailrecursion.blacktide.lookup-test
-  "Keyword call syntax determines cached IKeywordLookup versus ordinary ILookup
-  dispatch. Native emission must preserve call sites, effects and cache faults."
+  "Keyword syntax retains lookup dispatch, values and source effects.
+  Cache installation and unsuccessful probes are not observable contracts."
   (:require [clojure.test :refer [deftest is]]
             [com.tailrecursion.blacktide.backend-fixture :as backend]
-            [com.tailrecursion.blacktide.jvm-fixture])
-  (:import [com.tailrecursion.blacktide.jvm_fixture ObservedLookup]))
+            [com.tailrecursion.blacktide.compiler.passes.structure :as structure]))
+
+(deftype ObservedLookup [events entries]
+  clojure.lang.ILookup
+  (valAt [_ key] (swap! events conj [:val key]) (get entries key))
+  (valAt [_ key default] (swap! events conj [:default key]) (get entries key default))
+  clojure.lang.IKeywordLookup
+  (getLookupThunk [_ key]
+    (reify clojure.lang.ILookupThunk
+      (get [thunk target]
+        (if (instance? ObservedLookup target)
+          (do (swap! (.-events ^ObservedLookup target) conj [:get key])
+              (get (.-entries ^ObservedLookup target) key))
+          thunk)))))
 
 (deftype UncachedLookup [events entries]
   clojure.lang.ILookup
   (valAt [_ key] (swap! events conj [:val key]) (get entries key))
   (valAt [_ key default] (swap! events conj [:default key]) (get entries key default))
   clojure.lang.IKeywordLookup
-  (getLookupThunk [_ key] (swap! events conj [:no-thunk key]) nil))
+  (getLookupThunk [_ _] nil))
 
 (def ^:const constant-key :k)
 (defmacro lookup [target] `(:k ~target))
@@ -23,7 +35,7 @@
         targets [a a b c c {:k 11} nil a {} (java.util.HashMap. {:k 13})]]
     [(mapv #(function % events) targets) @events]))
 
-(deftest native-keyword-call-syntax
+(deftest keyword-call-syntax
   (doseq [body '[(:k (do (swap! events conj :target) target))
                 (:missing target)
                 (:k (do (swap! events conj :target) target) (do (swap! events conj :default) 17))
@@ -41,36 +53,7 @@
           :let [compiled ((backend/compile-program (list 'fn '[target events] (list 'loophole [] (wrap body))) mode) nil)]]
     (is (= (trace reference) (trace compiled)) (pr-str [body mode (wrap body)]))))
 
-(deftest keyword-sites-in-mutable-regions
-  (doseq [body '[(:k (do (swap! events conj :target) target))
-                (:missing target)
-                ((quote :k) target)
-                ((do :k) target)
-                (com.tailrecursion.blacktide.lookup-test/constant-key target)
-                (com.tailrecursion.blacktide.lookup-test/lookup target)]
-          mode [:default :dispatcher]
-          :let [reference (list 'fn '[target events] body)
-                compiled (list 'fn '[target events]
-                               (list 'loophole ['target 'target 'unused 0]
-                                     '(setq unused (inc unused)) body))]]
-    (is (= (trace ((backend/compile-program reference :default) nil))
-           (trace ((backend/compile-program compiled mode) nil)))
-        (pr-str [body mode]))))
-
-(deftest region-keyword-site-keeps-the-thunk-snapshot
-  (let [body '(:k (do (swap! events conj :target)
-                     (if (vector? target)
-                       (do (read-key (first target) events) (second target)) target)))
-        run (fn [body mode]
-              (let [f ((backend/compile-program (list 'fn 'read-key '[target events] body) mode) nil)
-                    events (atom []) a (ObservedLookup. events {:k 7})
-                    b (UncachedLookup. events {:k 9})]
-                [(f [a b] events) (f [b a] events) (f a events) @events]))]
-    (doseq [mode [:default :dispatcher]]
-      (is (= (run body :default)
-             (run (list 'loophole [] body) mode)) (str mode)))))
-
-(deftest keyword-sites-across-backward-jumps
+(deftest keyword-lookups-across-backward-jumps
   (let [reference '(fn [target events]
                      (loop [i 0 out []]
                        (if (< i 3) (recur (inc i) (conj out (:k target))) out)))
@@ -83,19 +66,22 @@
       (is (= (trace ((backend/compile-program reference :default) nil))
              (trace ((backend/compile-program source mode) nil))) (str mode)))))
 
-(deftest lookup-faults-are-not-bypassed
-  (let [target (reify
-                 clojure.lang.ILookup
-                 (valAt [_ _] :wrong-path)
-                 (valAt [_ _ _] :wrong-path)
-                 clojure.lang.IKeywordLookup
-                 (getLookupThunk [_ _] (throw (ex-info "lookup fault" {:phase :install}))))]
-    (doseq [mode [:default :dispatcher]
-            source '[(fn [target] (loophole [] (:k target)))
-                     (fn [target] (loophole [saved nil] (:k (setq saved target))))]
-            :let [f ((backend/compile-program source mode) nil)]]
-      (is (= {:phase :install}
-             (try (f target) (catch clojure.lang.ExceptionInfo e (ex-data e)))) (str mode)))))
+(deftest argument-and-lookup-failures
+  (doseq [mode [:default :dispatcher] fail-argument? [false true]]
+    (let [events (atom [])
+          target (reify clojure.lang.IKeywordLookup
+                   (getLookupThunk [_ _]
+                     (reify clojure.lang.ILookupThunk
+                       (get [_ _] (swap! events conj :lookup) (throw (ex-info "lookup" {:phase :lookup}))))))
+          f ((backend/compile-program
+               '(fn [target fail?]
+                  (loophole [saved nil]
+                    (:k (do (swap! events conj :argument)
+                            (when fail? (throw (ex-info "argument" {:phase :argument})))
+                            (setq saved target))))) mode) events)]
+      (is (= {:phase (if fail-argument? :argument :lookup)}
+             (try (f target fail-argument?) (catch clojure.lang.ExceptionInfo e (ex-data e)))))
+      (is (= (if fail-argument? [:argument] [:argument :lookup]) @events)))))
 
 (deftest assigned-keyword-targets
   (doseq [operator '[:k :missing (quote :k) (do :k)
@@ -120,7 +106,7 @@
     (is (= (trace ((backend/compile-program reference :default) nil))
            (trace ((backend/compile-program source mode) nil))) (str mode))))
 
-(deftest assigned-target-keeps-reentrant-snapshot
+(deftest reentrant-argument-evaluation
   (let [reference '(fn read-key [target events]
                      (:k (do (swap! events conj :target)
                              (if (vector? target)
@@ -134,8 +120,9 @@
               (let [f ((backend/compile-program form mode) nil)
                     events (atom []) a (ObservedLookup. events {:k 7}) b (UncachedLookup. events {:k 9})]
                 [(f [a b] events) (f [b a] events) (f a events) @events]))]
-    (doseq [mode [:default :dispatcher]]
-      (is (= (run reference :default) (run source mode)) (str mode)))))
+    (doseq [mode [:default :dispatcher]
+            form [source (list 'fn 'read-key '[target events] (list 'loophole [] (last reference)))]]
+      (is (= (run reference :default) (run form mode)) (str mode)))))
 
 (deftest skipped-lookup-and-reentry
   (let [reference '(fn [target events]
@@ -153,24 +140,6 @@
       (is (= (trace ((backend/compile-program reference :default) nil))
              (trace ((backend/compile-program source mode) nil))) (str mode)))))
 
-(defmacro twice-expanded [form]
-  (let [expanded (macroexpand form)] `(do ~expanded ~expanded)))
-
-(deftest cache-sites-survive-expansion-and-closure-instances
-  (doseq [[reference source]
-          '[[(fn [target events]
-               (com.tailrecursion.blacktide.lookup-test/twice-expanded (:k target)))
-             (fn [target events]
-               (com.tailrecursion.blacktide.lookup-test/twice-expanded
-                 (loophole [saved nil] (:k (setq saved target)))))]
-            [(let [factory (fn [] (fn [target events] (:k target)))]
-               (fn [target events] ((factory) target events)))
-             (let [factory (fn [] (fn [target events] (loophole [saved nil] (:k (setq saved target)))))]
-               (fn [target events] ((factory) target events)))]]
-          mode [:default :dispatcher]]
-    (is (= (trace ((backend/compile-program reference :default) nil))
-           (trace ((backend/compile-program source mode) nil))) (pr-str [source mode]))))
-
 (deftest assigned-lookup-after-multiple-scope-entries
   (let [reference '(fn [flag target events] [(if flag 3 7) (:k target)])
         source '(fn [flag target events]
@@ -183,5 +152,6 @@
               (let [f ((backend/compile-program form mode) nil)
                     events (atom []) target (ObservedLookup. events {:k 42})]
                 [(mapv #(f % target events) [true false false true]) @events]))]
+    (is (= :structured (:op (backend/capture #'structure/recover source))))
     (doseq [mode [:default :dispatcher]]
       (is (= (run reference :default) (run source mode)) (str mode)))))
diff --git a/test/com/tailrecursion/blacktide/native_sites_test.clj b/test/com/tailrecursion/blacktide/native_sites_test.clj
deleted file mode 100644
index 2372b13..0000000
--- a/test/com/tailrecursion/blacktide/native_sites_test.clj
+++ /dev/null
@@ -1,101 +0,0 @@
-(ns com.tailrecursion.blacktide.native-sites-test
-  "Control recovery retains Java function construction and cached lookup sites.
-  Generated loops preserve bound references without promising literal pooling."
-  (:require [clojure.test :refer [deftest is]]
-            [clojure.test.check.clojure-test :refer [defspec]]
-            [clojure.test.check.generators :as gen]
-            [clojure.test.check.properties :as prop]
-            [com.tailrecursion.blacktide.compiler.passes.structure :as structure]
-            [com.tailrecursion.blacktide.backend-fixture :as backend]
-            [com.tailrecursion.blacktide :refer [loophole setq label goto]]
-            [com.tailrecursion.blacktide.jvm-fixture :as jvm]))
-
-(def flags [true false false true])
-(defn identity-pattern [values]
-  (mapv (fn [a] (mapv #(identical? a %) values)) values))
-(deftest java-function-construction-sites
-  ;; Qualified method values and implicit functional-interface adapters also
-  ;; construct classes. A join must not split one source site across its paths.
-  (let [target (proxy [java.util.HashMap] [] (compute [_ callback] callback))
-        callback (fn [x y] [x y])]
-    (doseq [[tail invoke]
-            [['String/.length #(% "abc")]
-             [(with-meta 'String/valueOf {:param-tags ['Object]}) #(% "abc")]
-             [(with-meta 'String/new {:param-tags ['String]}) #(% "abc")]
-             ['(let [^java.util.function.BiFunction f callback] f)
-              #(.apply ^java.util.function.BiFunction % :left :right)]
-             ['(java.util.Map/.compute target :key callback)
-              #(.apply ^java.util.function.BiFunction % :left :right)]
-             ['(com.tailrecursion.blacktide.jvm-fixture/map-compute target :key callback)
-              #(.apply ^java.util.function.BiFunction % :left :right)]]
-            mode [:default :dispatcher]]
-      (let [ordinary (list 'let ['values '(if flag [1 2] [3 4])] (list 'vector 'values tail))
-            compiled (list 'loophole '[a 0 b 0]
-                           '(if flag (setq a 1 b 2) (setq a 3 b 4)) (list 'vector '[a b] tail))
-            observe (fn [body mode]
-                      (let [f ((backend/compile-program (list 'fn '[flag target callback] body) mode) nil)
-                            results (mapv #(f % target callback) flags)]
-                        [(identity-pattern (mapv (comp class second) results))
-                         (mapv (fn [[pair f]] [pair (invoke f)]) results)]))]
-        (is (= (observe ordinary :ordinary) (observe compiled mode)) (pr-str [mode tail]))))))
-
-(defn cached-lookups [mode wrapped?]
-  (let [lookup '(:k target)
-        body `(do (if ~'flag (swap! ~'events conj :yes) (swap! ~'events conj :no)) ~lookup)
-        events (atom []) target (jvm/->ObservedLookup events {:k 42})
-        f ((backend/compile-program (list 'fn '[flag target] (if wrapped? (list 'loophole [] body) body)) mode) events)
-        values (mapv #(f % target) flags)]
-    [values @events]))
-
-(deftest cached-lookup-sites-through-joins
-  (doseq [mode [:default :dispatcher]]
-    (is (= (cached-lookups mode false) (cached-lookups mode true))
-        (pr-str [mode]))))
-
-(defn loop-program [tail real-backedge?]
-  `(loophole [~'a 0 ~'b 0 ~'i 0]
-     (if ~'flag (setq ~'a 1 ~'b 2) (setq ~'a 3 ~'b 4))
-     (label ~'again)
-     ~(if real-backedge?
-        '(when (< i 1) (setq i (inc i)) (goto again))
-        '(when (neg? a) (goto again)))
-     (+ ~'a ~'b)
-     ~tail))
-
-(deftest bound-references-across-generated-loops
-  (doseq [real-backedge? [false true] context [:tail :expression]
-          mode [:default :dispatcher]]
-    (let [body (loop-program 'input real-backedge?)
-          source (list 'fn '[flag input] (if (= context :tail) body (list 'identity body)))
-          f ((backend/compile-program source mode) nil)
-          inputs [(Object.) (object-array [nil]) (Long/valueOf 1000)]
-          inputs (conj inputs (first inputs))]
-      (is (every? true? (map (fn [flag input] (identical? input (f flag input))) flags inputs))
-          (pr-str [real-backedge? context mode])))))
-
-(defn selection-tree [depth path]
-  (if (zero? depth)
-    (gen/fmap #(list 'do (list 'swap! 'events 'conj [:leaf path]) (list 'quote %))
-              (gen/elements [nil false 1000 1.5 :selected "text"]))
-    (let [test (gen/fmap #(list 'do (list 'swap! 'events 'conj [:test path])
-                              (list 'mod (list '+ 'input %) 3)) (gen/choose 0 4))
-          arm #(selection-tree (dec depth) (conj path %))]
-      (gen/one-of
-        [(gen/let [test test a (arm 0) b (arm 1)] (list 'if (list 'zero? test) a b))
-         (gen/let [test test a (arm 0) b (arm 1) c (arm 2)]
-           (list 'let ['choice test] (list 'case 'choice 0 a 1 b c)))]))))
-
-(defspec nested-joins-preserve-one-continuation {:num-tests 32 :seed 331662}
-  ;; Java method values after a join retain one class construction site.
-  (prop/for-all [tree (gen/bind (gen/choose 1 3) #(selection-tree % []))]
-    (let [body (list 'let ['picked tree] '(swap! events conj :joined) '[picked String/.length])
-          form (list 'loophole [] body)
-          source (fn [body] (list 'fn '[input] body))
-          observe (fn [body mode]
-                    (let [events (atom []) f ((backend/compile-program (source body) mode) events)
-                          values (mapv f (range 6))]
-                      [(mapv (fn [[picked f]] [picked (f "abc")]) values)
-                       (identity-pattern (mapv (comp class second) values)) @events]))
-          expected (observe body :default)]
-      (and (= :structured (:op (backend/capture #'structure/recover (source form))))
-           (every? #(= expected (observe form %)) [:default :dispatcher])))))
diff --git a/test/com/tailrecursion/blacktide/numeric_test.clj b/test/com/tailrecursion/blacktide/numeric_test.clj
index 3c66f6b..8cd51c4 100644
--- a/test/com/tailrecursion/blacktide/numeric_test.clj
+++ b/test/com/tailrecursion/blacktide/numeric_test.clj
@@ -11,6 +11,14 @@
 
 (def modes [:default :generic :dispatcher])
 
+(deftest numeric-results-retain-observed-origins
+  (doseq [expression '[(min x x) (max x x) (/ x 1) (/ 1 x)] mode modes
+          initial [Double/NaN '(double Double/NaN)]]
+    (let [form (list 'loophole ['x initial 'y '(double 0)]
+                     (list 'setq 'y expression) '(reset! events x) 'y)
+          f (backend/compile-program form mode) saved (atom nil) value (f saved)]
+      (is (identical? value @saved) (pr-str [mode initial expression])))))
+
 (deftest accumulator-recurrence-is-primitive
   (let [form '(loophole [sum 0]
                 (dotimes [i 10] (when (= i 5) (goto done)) (setq sum (+ sum i)))
@@ -62,9 +70,8 @@
                               (instance? Float value) (Float/floatToIntBits value)
                               (instance? BigDecimal value) (.toString ^BigDecimal value)
                               :else value)])
-       (catch Throwable error
-         ;; Helpful NPE text includes generated local names, which differ by backend.
-         [(class error) (when-not (instance? NullPointerException error) (ex-message error))])))
+       ;; HotSpot may omit built-in exception messages after repeated failures.
+       (catch Throwable error [(class error)])))
 
 (defn boxed [form]
   (list (with-meta 'clojure.lang.RT/box {:param-tags [Object]}) form))
diff --git a/test/com/tailrecursion/blacktide/package_test.clj b/test/com/tailrecursion/blacktide/package_test.clj
index 720be9e..eefdd65 100644
--- a/test/com/tailrecursion/blacktide/package_test.clj
+++ b/test/com/tailrecursion/blacktide/package_test.clj
@@ -10,13 +10,14 @@
         write! (fn [path value]
                  (let [file (io/file directory path)] (io/make-parents file) (spit file value)))]
     (try
-      (doseq [root ["deps.edn" "build.clj" "LICENSE" "src" "src-java"]
+      (doseq [root ["deps.edn" "build.clj" "LICENSE" "src"]
               file (file-seq (io/file root)) :when (.isFile file)]
         (let [target (io/file directory (.getPath file))]
           (io/make-parents target) (io/copy file target)))
       (write! "src/com/tailrecursion/.clj-kondo/hooks.clj" "(ns private-editor-hook)")
       (write! "src/com/tailrecursion/blacktide/.lsp/cache.json" "private editor state")
       (write! "resources/application.edn" "{:library-resource true}")
+      (write! "target/classes/stale.class" "unrelated build output")
       (let [java (str (io/file (System/getProperty "java.home") "bin" "java"))
             result (shell/sh "clojure" "-T:build" "jar" :dir directory
                              :env (assoc (into {} (System/getenv)) "JAVA_CMD" java))]
@@ -25,8 +26,7 @@
           (with-open [jar (java.util.jar.JarFile. (io/file directory "target/blacktide-0.1.0-SNAPSHOT.jar"))]
             (let [names (set (map #(.getName ^java.util.jar.JarEntry %) (enumeration-seq (.entries jar))))]
               (is (not-any? #(re-find #"(?:^|/)\." %) names))
-              (is (every? names ["com/tailrecursion/blacktide.clj"
-                                 "com/tailrecursion/blacktide/runtime/KeywordSite.class"
-                                 "META-INF/LICENSE" "application.edn"]))
+              (is (every? names ["com/tailrecursion/blacktide.clj" "META-INF/LICENSE" "application.edn"]))
+              (is (not-any? #(.endsWith ^String % ".class") names))
               (is (not-any? #(re-find #"(?:^|/)(?:test|tools|examples)/" %) names))))))
       (finally (doseq [file (reverse (file-seq directory))] (io/delete-file file))))))
diff --git a/test/com/tailrecursion/blacktide/source_loops_test.clj b/test/com/tailrecursion/blacktide/source_loops_test.clj
index 4159d51..832129c 100644
--- a/test/com/tailrecursion/blacktide/source_loops_test.clj
+++ b/test/com/tailrecursion/blacktide/source_loops_test.clj
@@ -18,15 +18,6 @@
     (sequential? value) (mapv canonical value)
     :else value))
 
-(defn error-message [error]
-  (let [message (ex-message error)]
-    ;; Prepared reference conversions use Class.cast. JVM checkcast has different
-    ;; wording; retain both failing types, not just the exception class.
-    (if (and message (instance? ClassCastException error))
-      (or (some-> (re-matches #"class (\S+) cannot be cast to class (\S+)(?: .*)?" message) rest vec)
-          (some-> (re-matches #"Cannot cast (\S+) to (\S+)" message) rest vec)
-          message)
-      message)))
 (defn enclose [body mode]
   ;; The helper functions belong to the caller. Only the loop is the region.
   (if (#{'let 'clojure.core/let} (first body))
@@ -44,7 +35,7 @@
   (if (map? f) f
     (let [events (atom [])]
       (try (let [value (f events input n)] {:value (canonical value) :class (class value) :trace (canonical @events)})
-           (catch Throwable e {:throw (class e) :message (error-message e) :trace (canonical @events)})))))
+           (catch Throwable e {:throw (class e) :trace (canonical @events)})))))
 (def bodies
   '[(let [step (fn ^long [^long x] (unchecked-inc x))]
       (loop [i 0 x 1000] (if (< i n) (recur (inc i) (step x)) [x (class x) (identical? x x)])))
diff --git a/test/com/tailrecursion/nanopass/annotations.clj b/test/com/tailrecursion/nanopass/annotations.clj
index 142b410..8133305 100644
--- a/test/com/tailrecursion/nanopass/annotations.clj
+++ b/test/com/tailrecursion/nanopass/annotations.clj
@@ -4,11 +4,8 @@
             [com.tailrecursion.nanopass :as np]
             [com.tailrecursion.nanopass.pipeline :as pipeline]
             [com.tailrecursion.nanopass.fixtures :as f]
-            [com.tailrecursion.nanopass.passes :as p]
-            [com.tailrecursion.nanopass.clients.assignment :as assignment]))
+            [com.tailrecursion.nanopass.passes :as p]))
 
 (np/ann-languages f/Source f/Core f/Literals f/Mutual f/Stopped)
 (np/ann-passes pipeline/map-value)
 (np/ann-passes p/lower p/lower-binding p/rename p/rename-binding p/count-step p/lower-envelope)
-(np/ann-languages assignment/Source assignment/Core)
-(np/ann-passes assignment/lower assignment/sequential-writes assignment/parallel-writes)
diff --git a/test/com/tailrecursion/nanopass/assignment_test.clj b/test/com/tailrecursion/nanopass/assignment_test.clj
deleted file mode 100644
index 918b62c..0000000
--- a/test/com/tailrecursion/nanopass/assignment_test.clj
+++ /dev/null
@@ -1,64 +0,0 @@
-(ns com.tailrecursion.nanopass.assignment-test
-  "Execute both grammars against explicit traces and deterministic generated inputs.
-  The interpreter is test instrumentation; the compiler pass only constructs data."
-  (:require [clojure.test :refer [deftest is testing]]
-            [clojure.test.check :as tc]
-            [clojure.test.check.properties :as prop]
-            [com.tailrecursion.nanopass.clients.assignment :as lower]
-            [com.tailrecursion.assignment-model :refer [lit read-var batch sequence-of event execute expression]]))
-
-(deftest values-and-sequencing
-  (let [x (read-var 'x) y (read-var 'y)]
-    (doseq [[name source value vars trace]
-            [[:empty-sequential (batch :setq) nil {'x 1 'y 2} []]
-             [:empty-parallel (batch :psetq) nil {'x 1 'y 2} []]
-             [:sequential (batch :setq 'x (lit 3) 'y x) 3 {'x 3 'y 3} []]
-             [:swap (batch :psetq 'x y 'y x) nil {'x 2 'y 1} []]
-             [:repeated (batch :psetq 'x (event :first (lit 9)) 'x (event :second y))
-              nil {'x 2 'y 2} [[:first 9] [:second 2]]]
-             [:nested (batch :psetq 'x y 'y (batch :setq 'x (lit 9)))
-              nil {'x 2 'y 9} []]
-             [:nested-parallel (batch :psetq 'x (sequence-of (batch :psetq 'x y 'y x) y) 'y x)
-              nil {'x 1 'y 2} []]
-             [:sequential-trace (batch :setq 'x (event :first y) 'y (event :second x))
-              2 {'x 2 'y 2} [[:first 2] [:second 2]]]
-             [:parallel-trace (batch :psetq 'x (event :first y) 'y (event :second x))
-              nil {'x 2 'y 1} [[:first 2] [:second 1]]]
-             [:branch {:op :if :test (batch :psetq 'x (lit false))
-                       :then (event :wrong (lit 0))
-                       :else (batch :setq 'y (event :chosen x))}
-              false {'x false 'y false} [[:chosen false]]]
-             [:snapshot {:op :add :left x :right (batch :setq 'x (lit 10))}
-              11 {'x 10 'y 2} []]
-             [:runtime-type (batch :setq 'x (lit "string") 'y x)
-              "string" {'x "string" 'y "string"} []]
-             [:quoted-ir (lit {:op :psetq :writes :data})
-              {:op :psetq :writes :data} {'x 1 'y 2} []]]]
-      (testing (str name)
-        (let [expected {:value value :vars vars :trace trace :status :return}]
-          (is (= expected (execute source {'x 1 'y 2})))
-          (is (= expected (execute (lower/lower source) {'x 1 'y 2}))))))))
-
-(deftest abandoned-writes
-  (doseq [op [:setq :psetq] nested? [false true]]
-    (let [source (batch op 'x (event :first (lit 10))
-                          'y (sequence-of
-                               (if nested? (batch :setq 'x (lit 9)) (lit nil))
-                               {:op :throw :tag :failed}))
-          expected {:status :throw :tag :failed
-                    :vars {'x (cond nested? 9 (= op :setq) 10 :else 1) 'y 2}
-                    :trace [[:first 10]]}]
-      (testing (pr-str [op nested?])
-        (is (= expected (execute source {'x 1 'y 2})))
-        (is (= expected (execute (lower/lower source) {'x 1 'y 2})))))))
-
-(deftest generated-assignment-combinations
-  (let [property (prop/for-all [source (expression 4)]
-                   (let [lowered (lower/lower source)]
-                     (every? true?
-                       (for [[x y] [[0 0] [1 2] [-7 5] [10 -3]]
-                             :let [vars {'x x 'y y}]]
-                         (= (execute source vars) (execute lowered vars))))))]
-    (doseq [seed [712381 314159]]
-      (let [result (tc/quick-check 200 property :seed seed)]
-        (is (:pass? result) (pr-str result))))))
diff --git a/test/com/tailrecursion/nanopass/check.clj b/test/com/tailrecursion/nanopass/check.clj
index 5b6892c..0db0ce9 100644
--- a/test/com/tailrecursion/nanopass/check.clj
+++ b/test/com/tailrecursion/nanopass/check.clj
@@ -7,9 +7,7 @@
             [com.tailrecursion.nanopass.pipeline :as pipeline]
             [com.tailrecursion.nanopass.annotations]
             [com.tailrecursion.nanopass.fixtures :as f]
-            [com.tailrecursion.nanopass.passes :as p]
-            [com.tailrecursion.nanopass.clients.assignment :as assignment]
-            [com.tailrecursion.assignment-model :as semantics]))
+            [com.tailrecursion.nanopass.passes :as p]))
 
 (defn check-form [form expected]
   (binding [*ns* (the-ns 'com.tailrecursion.nanopass.check)
@@ -60,8 +58,7 @@
 
 (deftest checked-passes
   (is (t/check-ns-clj 'com.tailrecursion.nanopass.pipeline))
-  (is (t/check-ns-clj 'com.tailrecursion.nanopass.passes))
-  (is (t/check-ns-clj 'com.tailrecursion.nanopass.clients.assignment)))
+  (is (t/check-ns-clj 'com.tailrecursion.nanopass.passes)))
 
 (deftest pipeline-contracts
   (doseq [[accepted form]
@@ -122,30 +119,3 @@
             (macroexpand-1 (cons 'np/defpasses definitions)))
           form (list head bindings signatures 'expression)]
       (checks? '[f/Source-Binding :-> f/Core-Binding] form accepted))))
-
-(deftest assignment-contract-probes
-  (doseq [form
-          '[(fn [node] node)
-            (fn [_] {:op :psetq :writes []})
-            (fn [node] {:op :if :test {:op :const :value true} :then node :else {:op :const :value nil}})
-            (fn [_] {:op :write :binding 'x})
-            (fn [_] {:op :write :binding 42 :value {:op :const :value nil}})
-            (fn [_] {:op :write :binding 'x :value {:op :const :value nil} :extra true})
-            (fn [node] (assignment/lower (assignment/lower node)))
-            (fn [node] (case (:op node) :const node
-                         ((fn [^{:typed.clojure/- typed.clojure/Nothing} missing]
-                            (throw (ex-info "unhandled" {:node missing}))) node)))]]
-    (checks? '[assignment/Source-Expr :-> assignment/Core-Expr] form false)))
-
-(deftest structural-types-do-not-prove-order
-  (let [form '(fn [node]
-                (if (= :psetq (:op node))
-                  {:op :do :forms [(assignment/sequential-writes (:writes node)) {:op :const :value nil}]}
-                  (assignment/lower node)))
-        {:keys [ex delayed-errors]} (check-form form '[assignment/Source-Expr :-> assignment/Core-Expr])
-        wrong (binding [*ns* (the-ns 'com.tailrecursion.nanopass.check)] (eval form))
-        source (semantics/batch :psetq 'x (semantics/read-var 'y) 'y (semantics/read-var 'x))]
-    (is (nil? ex) (str ex))
-    (is (empty? delayed-errors))
-    (is (= {'x 2 'y 2} (:vars (semantics/execute (wrong source) {'x 1 'y 2}))))
-    (is (= {'x 2 'y 1} (:vars (semantics/execute (assignment/lower source) {'x 1 'y 2}))))))
diff --git a/test/com/tailrecursion/nanopass/clients/assignment.clj b/test/com/tailrecursion/nanopass/clients/assignment.clj
deleted file mode 100644
index e090ffb..0000000
--- a/test/com/tailrecursion/nanopass/clients/assignment.clj
+++ /dev/null
@@ -1,46 +0,0 @@
-(ns ^:typed.clojure com.tailrecursion.nanopass.clients.assignment
-  "A language client lowering sequential/parallel assignment to writes and temps.
-  This compiler constructs immutable data and has no dependency on Loophole."
-  (:require [com.tailrecursion.nanopass :as np]
-            [typed.clojure :as-alias t]))
-
-(np/deflanguage Source
-  {Expr (t/U (t/HMap :mandatory {:op ':const :value t/Any} :complete? true)
-             (t/HMap :mandatory {:op ':read :binding t/Sym} :complete? true)
-             (t/HMap :mandatory {:op ':throw :tag t/Keyword} :complete? true)
-             (t/HMap :mandatory {:op ':add :left Expr :right Expr} :complete? true)
-             (t/HMap :mandatory {:op ':do :forms (t/Vec Expr)} :complete? true)
-             (t/HMap :mandatory {:op ':if :test Expr :then Expr :else Expr} :complete? true)
-             (t/HMap :mandatory {:op ':event :tag t/Keyword :value Expr} :complete? true)
-             (t/HMap :mandatory {:op ':setq :writes Writes} :complete? true)
-             (t/HMap :mandatory {:op ':psetq :writes Writes} :complete? true))
-   Writes (t/Vec (t/HVec [t/Sym Expr]))})
-
-(np/derive-language Core Source
-  {:remove {Expr #{:setq :psetq}}
-   :add {Expr [(t/HMap :mandatory {:op ':write :binding t/Sym :value Expr} :complete? true)
-               (t/HMap :mandatory {:op ':temp :id t/Sym} :complete? true)
-               (t/HMap :mandatory {:op ':bind :id t/Sym :value Expr :body Expr} :complete? true)]}})
-
-(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)})
-
-  (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)))))
-
-  (lower {:from [Source Expr] :to [Core Expr]}
-    [node]
-    :setq (sequential-writes (:writes node))
-    :psetq (parallel-writes (:writes node))))
diff --git a/test/com/tailrecursion/test_runner.clj b/test/com/tailrecursion/test_runner.clj
index abd6467..55c6415 100644
--- a/test/com/tailrecursion/test_runner.clj
+++ b/test/com/tailrecursion/test_runner.clj
@@ -5,7 +5,6 @@
 (def suites
   '[com.tailrecursion.nanopass.language-test
     com.tailrecursion.nanopass.pass-test
-    com.tailrecursion.nanopass.assignment-test
     com.tailrecursion.blacktide.machine-test
     com.tailrecursion.blacktide.backend-test
     com.tailrecursion.blacktide.numeric-test
@@ -26,7 +25,7 @@
     com.tailrecursion.blacktide.case-test
     com.tailrecursion.blacktide.lookup-test
     com.tailrecursion.blacktide.invocation-test
-    com.tailrecursion.blacktide.native-sites-test
+    com.tailrecursion.blacktide.interop-values-test
     com.tailrecursion.blacktide.algorithms-test])
 
 (defn -main [& selected]