build: use jerbuild for native jcode builds

ober

7330c2fed0f64c20a375a00a1426f761bca53ee6

diff --git a/.build.yml b/.build.yml
index ea93fd6..30c3e22 100644
--- a/.build.yml
+++ b/.build.yml
@@ -5,11 +5,7 @@ packages:
   - uuid-dev
   - libz-dev
   - liblz4-dev
-  - libpcre2-dev
-  - libsqlite3-dev
-  - libssl-dev
   - libx11-dev
-  - pkg-config
   - git
   - curl
   - unzip
@@ -26,27 +22,20 @@ tasks:
       # install one via rustup and persist it onto PATH for every later task.
       curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y --profile minimal --default-toolchain stable
       echo 'source "$HOME/.cargo/env"' >> ~/.buildenv
-  - bootstrap-chez: |
+  - install-jerbuild: |
       cd jerboa
-      # Build the vendored Chez Scheme locally into ~/jerboa/.chez.
-      # `make chez` is the documented bootstrap entry point.
-      make chez
-  - build-jerboa: |
-      cd jerboa
-      # Build the Rust native lib FIRST. The stdlib libraries jcode imports
-      # bind its FFI symbols at visit time, so libjerboa_native must already be
-      # in lib/ before anything importing them is compiled.
-      make native
-      make build
+      make jerboa
+      make install PREFIX=$HOME/.local
+      echo 'export PATH="$HOME/.local/bin:$PATH"' >> ~/.buildenv
   - vendor-code: |
       cd jerboa-code
       mkdir -p vendor
-      [ -d vendor/chez-sqlite ] || git clone --depth 1 https://git.sr.ht/~lisp/chez-sqlite vendor/chez-sqlite
+      [ -d vendor/jerboa-sqlite ] || git clone --depth 1 https://git.sr.ht/~lisp/jerboa-sqlite vendor/jerboa-sqlite
       [ -d vendor/termbox2 ] || git clone --depth 1 https://github.com/termbox/termbox2.git vendor/termbox2
       [ -d vendor/jerboa-websearch ] || git clone --depth 1 https://git.sr.ht/~lisp/jerboa-websearch vendor/jerboa-websearch
   - build-code: |
       cd jerboa-code
-      JERBOA_HOME=$HOME/jerboa make build
+      env -u JERBOA_HOME make build
   - test-code: |
       cd jerboa-code
-      JERBOA_HOME=$HOME/jerboa make test
+      env -u JERBOA_HOME make test
diff --git a/.gitignore b/.gitignore
index f4cf582..51e7b31 100644
--- a/.gitignore
+++ b/.gitignore
@@ -11,6 +11,7 @@ jcode.json
 
 # Generated Scheme libraries (built from src/**/*.ss by jerbuild)
 /lib/
+/support/sqlite-bundled/target/
 /jcode_tui_shim.dylib
 /jcode_tui_shim.so
 
diff --git a/.jerbuild b/.jerbuild
index 222ffdb..9160887 100644
--- a/.jerbuild
+++ b/.jerbuild
@@ -3,20 +3,18 @@
 ;; jerbuild bundles Chez Scheme + the jerboa stdlib, so NO jerboa source
 ;; checkout or external Chez is required. The only external input is the native
 ;; Rust lib (libjerboa_native), built from vendor/jerboa-native-rs with the
-;; features jcode needs (tls + sqlite + crypto).
+;; features jcode needs (tls + crypto). SQLite comes from jerboa-sqlite's C
+;; shim so the binary does not link duplicate jerboa_sqlite_* symbols.
 ;;
-;; Static FFI needs NO source patching: chez-sqlite.sls falls through to (void)
+;; Static FFI needs NO source patching: jerboa-sqlite.sls falls through to (void)
 ;; and tui-ffi.ss honors JERBOA_STATIC=1 (set by support/jcode-main.c), so the
 ;; linked-in shim symbols resolve via the registered FFI symbols below.
 
 (entry  "main-binary.ss")
 (output "jcode")
-(requires "cargo" "cc" "sqlite3 headers/libs" "vendor/jerboa-native-rs")
-(notes "jerboa-native-rs is built with no default features and tls,sqlite,crypto only. DuckDB is intentionally not required.")
-
 ;; do-binary-build auto-appends the bundled stdlib; list only project + vendor.
 (libdirs "lib"
-         "vendor/chez-sqlite/src"
+         "vendor/jerboa-sqlite/lib"
          "vendor/jerboa-websearch/src")
 
 ;; Symbols statically registered via Sforeign_symbol (sqlite shim, termbox TUI
@@ -26,15 +24,18 @@
 ;; Stock jerbuild main.c + setenv(JERBOA_STATIC=1); see the file's header.
 (main-c "support/jcode-main.c")
 
-;; C shims compiled into the binary. sqlite3.h is provided by the macOS SDK /
-;; system; add an -I here if a host needs an explicit path.
+;; C shims compiled into the binary. SQLite comes from the Cargo-built bundled
+;; archive below, with support/sqlite3.h providing the small C API surface used
+;; by the shim.
 (extra-sources
-  "vendor/chez-sqlite/chez_sqlite_shim.c"
+  ("vendor/jerboa-sqlite/jerboa_sqlite_shim.c" cflags: "-Isupport")
   ("src/jcode/ui/jcode_tui_shim.c" cflags: "-DTB_OPT_ATTR_W=32 -Ivendor/termbox2"))
 
-;; jerboa-native (Rust): tls + sqlite + crypto only (no duckdb/pcap/postgres). jerbuild
+(extra-archives "support/sqlite-bundled/target/release/libjcode_sqlite_bundled.a")
+
+;; jerboa-native (Rust): tls + crypto only (no sqlite/duckdb/pcap/postgres). jerbuild
 ;; runs cargo and links the resulting libjerboa_native.a.
 (rust-crates
   ("vendor/jerboa-native-rs/Cargo.toml"
-   features: "tls,sqlite,crypto"
+   features: "tls,crypto"
    no-default-features: #t))
diff --git a/.jerbuild.freebsd-amd64 b/.jerbuild.freebsd-amd64
new file mode 100644
index 0000000..65c0c3b
--- /dev/null
+++ b/.jerbuild.freebsd-amd64
@@ -0,0 +1,36 @@
+;; jerbuild build config for jerboa-code (jcode) -- native FreeBSD amd64.
+;;
+;; Build on a FreeBSD amd64 host with:
+;;   jerbuild build --config .jerbuild.freebsd-amd64
+;;
+;; This uses only installed jerbuild, cc, cargo, and project/vendor deps.
+
+(entry  "main-binary.ss")
+(output "jcode-freebsd-amd64")
+
+(cc "cc")
+
+;; FreeBSD has dlopen in libc. -lutil covers openpty, termcap backs Chez's
+;; expeditor, and GNU libiconv is installed under /usr/local/lib.
+(os-libs "-lm -lpthread -lutil -ltermcap -L/usr/local/lib -liconv")
+
+(libdirs "lib"
+         "vendor/jerboa-sqlite/lib"
+         "vendor/jerboa-websearch/src")
+
+(ffi-symbols "support/ffi-symbols.list")
+
+(main-c "support/jcode-main.c")
+
+(extra-sources
+  ("vendor/jerboa-sqlite/jerboa_sqlite_shim.c" cflags: "-Isupport")
+  ("src/jcode/ui/jcode_tui_shim.c" cflags: "-DTB_OPT_ATTR_W=32 -Ivendor/termbox2"))
+
+(extra-archives "support/sqlite-bundled/target/release/libjcode_sqlite_bundled.a")
+
+(extra-ldflags "-Wl,--export-dynamic")
+
+(rust-crates
+  ("vendor/jerboa-native-rs/Cargo.toml"
+   features: "tls,crypto"
+   no-default-features: #t))
diff --git a/.jerbuild.linux-amd64 b/.jerbuild.linux-amd64
new file mode 100644
index 0000000..04df4d1
--- /dev/null
+++ b/.jerbuild.linux-amd64
@@ -0,0 +1,34 @@
+;; jerbuild build config for jerboa-code (jcode) -- native Linux amd64.
+;;
+;; Build on a Linux amd64 host with:
+;;   jerbuild build --config .jerbuild.linux-amd64
+;;
+;; This uses only installed jerbuild, cc, cargo, and project/vendor deps.
+
+(entry  "main-binary.ss")
+(output "jcode-linux-amd64")
+
+(cc "cc")
+
+(os-libs "-lm -ldl -lpthread -luuid -lncurses -lstdc++")
+
+(libdirs "lib"
+         "vendor/jerboa-sqlite/lib"
+         "vendor/jerboa-websearch/src")
+
+(ffi-symbols "support/ffi-symbols.list")
+
+(main-c "support/jcode-main.c")
+
+(extra-sources
+  ("vendor/jerboa-sqlite/jerboa_sqlite_shim.c" cflags: "-Isupport")
+  ("src/jcode/ui/jcode_tui_shim.c" cflags: "-DTB_OPT_ATTR_W=32 -Ivendor/termbox2"))
+
+(extra-archives "support/sqlite-bundled/target/release/libjcode_sqlite_bundled.a")
+
+(extra-ldflags "-Wl,--export-dynamic")
+
+(rust-crates
+  ("vendor/jerboa-native-rs/Cargo.toml"
+   features: "tls,crypto"
+   no-default-features: #t))
diff --git a/.jerbuild.linux-arm64 b/.jerbuild.linux-arm64
new file mode 100644
index 0000000..3da8a0b
--- /dev/null
+++ b/.jerbuild.linux-arm64
@@ -0,0 +1,34 @@
+;; jerbuild build config for jerboa-code (jcode) -- native Linux arm64.
+;;
+;; Build on a Linux arm64/aarch64 host with:
+;;   jerbuild build --config .jerbuild.linux-arm64
+;;
+;; This uses only installed jerbuild, cc, cargo, and project/vendor deps.
+
+(entry  "main-binary.ss")
+(output "jcode-linux-arm64")
+
+(cc "cc")
+
+(os-libs "-lm -ldl -lpthread -luuid -lncurses -lstdc++")
+
+(libdirs "lib"
+         "vendor/jerboa-sqlite/lib"
+         "vendor/jerboa-websearch/src")
+
+(ffi-symbols "support/ffi-symbols.list")
+
+(main-c "support/jcode-main.c")
+
+(extra-sources
+  ("vendor/jerboa-sqlite/jerboa_sqlite_shim.c" cflags: "-Isupport")
+  ("src/jcode/ui/jcode_tui_shim.c" cflags: "-DTB_OPT_ATTR_W=32 -Ivendor/termbox2"))
+
+(extra-archives "support/sqlite-bundled/target/release/libjcode_sqlite_bundled.a")
+
+(extra-ldflags "-Wl,--export-dynamic")
+
+(rust-crates
+  ("vendor/jerboa-native-rs/Cargo.toml"
+   features: "tls,crypto"
+   no-default-features: #t))
diff --git a/Dockerfile b/Dockerfile
deleted file mode 100644
index 229a964..0000000
--- a/Dockerfile
+++ /dev/null
@@ -1,59 +0,0 @@
-# Dockerfile — Build jcode-musl using the jerboa21/jerboa base image
-#
-# Produces a fully static binary with zero runtime dependencies.
-# No Chez Scheme or Jerboa installation needed on the target host.
-#
-# The base image (jerboa21/jerboa) provides stock Chez, musl Chez,
-# jerboa libs, and all build dependencies pre-installed.
-#
-# Usage:
-#   docker build -t jcode-builder .
-#   docker run --rm jcode-builder > jcode-musl && chmod +x jcode-musl
-#
-# Or extract via docker cp:
-#   docker build -t jcode-builder .
-#   id=$(docker create jcode-builder)
-#   docker cp $id:/out/jcode-musl ./jcode-musl
-#   docker rm $id
-
-FROM jerboa21/jerboa AS builder
-
-ARG CACHE_BUST
-
-# ── Overlay the host's ~/mine/jerboa over the base image's bundled copy ─────
-# The base image's /build/mine/jerboa is a snapshot and lags reality as soon
-# as a new module lands upstream (e.g. (std os errno) added on 2026-05-13).
-# Without this overlay the build fails with "library (std os errno) not
-# found" every time jcode source references a fresh jerboa module.
-#
-# Source: --build-context host-jerboa=<path> from the Makefile docker target.
-# We delete any compiled .so/.wpo from the overlay so the freshly-copied
-# sources get recompiled against the container's Chez (a host-built .so
-# from a different libc/version would be wrong inside the container).
-COPY --from=host-jerboa . /build/mine/jerboa-host-overlay
-RUN find /build/mine/jerboa-host-overlay \( -name '*.so' -o -name '*.wpo' \) -delete 2>/dev/null; \
-    rm -rf /build/mine/jerboa && \
-    mv /build/mine/jerboa-host-overlay /build/mine/jerboa && \
-    find /build/mine/jerboa/lib \( -name '*.so' -o -name '*.wpo' \) -delete 2>/dev/null; true
-
-# ── Copy jcode source ────────────────────────────────────────────────────────
-COPY . /build/mine/jerboa-code
-
-# ── Build jcode-musl ─────────────────────────────────────────────────────────
-WORKDIR /build/mine/jerboa-code
-RUN JERBOA_HOME=/build/mine/jerboa make linux-local
-
-# ── Verify ───────────────────────────────────────────────────────────────────
-RUN echo "--- Binary info ---" && \
-    ls -lh jcode-musl && \
-    file jcode-musl && \
-    echo "--- Hardening checks ---" && \
-    { file jcode-musl | grep -qE 'stripped|no section header' && echo "  PASS: stripped" || echo "  WARN: not stripped"; } && \
-    echo "--- Path leak check ---" && \
-    count=$(strings jcode-musl | grep -c '/home/' || true) && \
-    { [ "$count" -gt 0 ] && echo "  WARNING: home paths found ($count)" || echo "  PASS: no home path leaks"; }
-
-# ── Output ───────────────────────────────────────────────────────────────────
-FROM ubuntu:24.04
-COPY --from=builder /build/mine/jerboa-code/jcode-musl /out/jcode-musl
-CMD ["cat", "/out/jcode-musl"]
diff --git a/Makefile b/Makefile
index 0c20413..2cbdfd1 100644
--- a/Makefile
+++ b/Makefile
@@ -10,45 +10,43 @@ ifeq ($(JH),)
 $(error jerbuild not found on PATH (or '$(JERBUILD) --jerboa-home' failed). Install jerbuild, or set JERBUILD=/path/to/jerbuild)
 endif
 
-# JERBOA_HOME / SCHEME are used ONLY by the cross-compile targets
-# (linux*/freebsd*/android) and the legacy `binary-legacy` fallback — the host
-# dev loop and `make binary` no longer touch them.
-JERBOA_HOME ?= $(HOME)/mine/jerboa
-SCHEME      ?= $(JERBOA_HOME)/.chez/bin/scheme
-FREEBSD_AMD64_CC ?= $(JERBOA_HOME)/support/cross-cc-freebsd-amd64
-
-LIBDIRS := --libdirs ./lib:vendor/chez-sqlite/src:vendor/jerboa-websearch/src:$(JH)/lib
+LIBDIRS := --libdirs ./lib:vendor/jerboa-sqlite/lib:vendor/jerboa-websearch/src:$(JH)/lib
 JEXEC   := $(JERBUILD) exec $(LIBDIRS)
 
 # Library paths for FFI shared objects (macOS: dylib, Linux: so). The native
 # Rust lib now lives in ./lib (dropped by the native-rs target), matching the
 # (std crypto native-rust) loader's CWD-relative "lib/" fallback.
-SQLITE_LIB_DIR := $(shell brew --prefix sqlite 2>/dev/null)/lib
-SHIM_DIR       := $(CURDIR)/vendor/chez-sqlite
+SHIM_DIR       := $(CURDIR)/vendor/jerboa-sqlite
+SQLITE_STAGE   := vendor/jerboa-sqlite/.jcode-src
+SQLITE_BUNDLED_DIR := support/sqlite-bundled
+SQLITE_BUNDLED_A   := $(SQLITE_BUNDLED_DIR)/target/release/libjcode_sqlite_bundled.a
 TUI_SHIM_DIR   := $(CURDIR)/vendor/termbox2
 NATIVE_LIB_DIR := $(CURDIR)/lib
-LDPATH         := $(SHIM_DIR):$(TUI_SHIM_DIR):$(SQLITE_LIB_DIR):$(NATIVE_LIB_DIR)
+LDPATH         := $(SHIM_DIR):$(TUI_SHIM_DIR):$(NATIVE_LIB_DIR)
 
 # Native Rust lib (vendored crate, built with cargo). Only the features jcode
 # uses — tls + crypto; no duckdb/pcap/postgres.
 NATIVE_DIR      := vendor/jerboa-native-rs
 NATIVE_FEATURES ?= tls,crypto
+comma := ,
+NATIVE_FEATURES_TAG := $(subst $(comma),-,$(NATIVE_FEATURES))
 NATIVE_A        := $(NATIVE_DIR)/target/release/libjerboa_native.a
 NATIVE_DYLIB    := $(NATIVE_DIR)/target/release/libjerboa_native.dylib
 NATIVE_SO       := $(NATIVE_DIR)/target/release/libjerboa_native.so
+NATIVE_SENTINEL := $(NATIVE_DIR)/target/release/.built-with-$(NATIVE_FEATURES_TAG)
 
 # Per-OS link flags handed to `jerbuild build --os-libs`. jerbuild appends
 # these after the Chez kernel + our archives when linking the binary.
 UNAME_S := $(shell uname -s)
 ifeq ($(UNAME_S),Darwin)
-JCODE_OS_LIBS := -lm -lpthread -lncurses -liconv -lc++ -L$(SQLITE_LIB_DIR) -lsqlite3 -framework Security -framework CoreFoundation
+JCODE_OS_LIBS := -lm -lpthread -lncurses -liconv -lc++ -framework Security -framework CoreFoundation
 else ifeq ($(UNAME_S),FreeBSD)
-JCODE_OS_LIBS := -lm -lpthread -lncurses -L/usr/local/lib -liconv -lsqlite3
+JCODE_OS_LIBS := -lm -lpthread -lutil -lncurses -L/usr/local/lib -liconv
 else
-JCODE_OS_LIBS := -lm -ldl -lpthread -luuid -lncurses -lsqlite3 -lstdc++
+JCODE_OS_LIBS := -lm -ldl -lpthread -luuid -lncurses -lstdc++
 endif
 
-.PHONY: all help build gen run test test-providers clean repl binary binary-legacy install tui-shim run-tui native-rs jcode-musl linux linux-amd64 linux-arm64 jcode-linux-amd64 linux-docker linux-check linux-local docker test-linux test-linux-amd64 purge-stale sqlite-shim android android-clean vendor-deps vendor-clean
+.PHONY: all help build gen run test test-providers clean repl binary install tui-shim run-tui native-rs sqlite-bundled linux linux-amd64 linux-arm64 jcode-linux-amd64 jcode-linux-arm64 jcode-linux-native jcode-linux-arm64-native freebsd freebsd-amd64 jcode-freebsd-amd64 jcode-freebsd-native purge-stale sqlite-shim sqlite-lib android android-clean vendor-deps vendor-clean
 
 all: help
 
@@ -62,19 +60,17 @@ help:
 	@echo "  repl         Open a bare Scheme REPL with project libdirs"
 	@echo "  gen          Transpile src/ → lib/ + rebuild sqlite shim (jerbuild)"
 	@echo "  native-rs    Build vendored libjerboa_native (cargo, tls+crypto)"
+	@echo "  sqlite-bundled Build bundled SQLite archive via cargo"
 	@echo "  binary       Build native binary via 'jerbuild build' (.jerbuild)"
-	@echo "  binary-legacy  Fallback binary via build-binary.ss (needs jerboa checkout)"
 	@echo "  install      Install binary to ~/.local/bin"
-	@echo "  linux        Build Linux x86_64 static binary (cross-compile from this host)"
-	@echo "  linux-amd64  Cross-compile static jcode-linux-amd64 (default)"
-	@echo "  linux-arm64  Cross-compile static jcode-linux-arm64"
-	@echo "  linux-docker Build static musl binary via Docker (slower; canonical)"
-	@echo "  linux-local  Build static musl binary on host (Linux only)"
-	@echo "  linux-check  Fast drift check for musl build path"
-	@echo "  test-linux   Smoke-test the cross-compiled binary under podman/qemu"
+	@echo "  linux        Build Linux x86_64 binary with jerbuild (run on Linux)"
+	@echo "  linux-amd64  Build native Linux x86_64 jcode-linux-amd64 with jerbuild"
+	@echo "  linux-arm64  Build native Linux arm64 jcode-linux-arm64 with jerbuild"
+	@echo "  freebsd      Build FreeBSD amd64 binary with jerbuild (run on FreeBSD)"
 	@echo "  clean        Remove compiled artifacts and binaries"
 	@echo "  purge-stale  Remove stale .so/.wpo files"
-	@echo "  sqlite-shim  Rebuild chez_sqlite_shim.so"
+	@echo "  sqlite-shim  Rebuild jerboa_sqlite_shim.so"
+	@echo "  sqlite-lib   Transpile vendor/jerboa-sqlite src/ → lib/"
 	@echo "  tui-shim     Rebuild jcode_tui_shim"
 	@echo "  android      Build Android APK in Termux (android/build/*.apk)"
 	@echo "  android-clean  Remove android/build/"
@@ -85,7 +81,7 @@ help:
 # to the class of bug where a stale .so silently linked against an old
 # shim causes a runtime "no entry for X" foreign-procedure error.
 #
-# Always also clear the prebuilt vendor/chez-sqlite artifacts — they pin
+# Always also clear the prebuilt vendor/jerboa-sqlite artifacts — they pin
 # absolute paths to the shim and are cheap to recompile.
 purge-stale:
 	@find lib -name "*.so" -o -name "*.wpo" 2>/dev/null | while read f; do \
@@ -105,21 +101,40 @@ purge-stale:
 	    break; \
 	  fi; \
 	done
-	@rm -f vendor/chez-sqlite/src/chez-sqlite.so vendor/chez-sqlite/src/chez-sqlite.wpo
+	@rm -f vendor/jerboa-sqlite/lib/jerboa-sqlite.so vendor/jerboa-sqlite/lib/jerboa-sqlite.wpo
 
-# ── chez-sqlite shim build ──────────────────────────────────────────────────
+# ── jerboa-sqlite shim build ──────────────────────────────────────────────────
 # Rebuild the FFI shim if its C source is newer than the .so. This catches
-# the case where vendor/chez-sqlite is updated but the .so isn't refreshed.
-sqlite-shim: vendor/chez-sqlite
-	@if [ ! -f vendor/chez-sqlite/chez_sqlite_shim.so ] || \
-	    [ vendor/chez-sqlite/chez_sqlite_shim.c -nt vendor/chez-sqlite/chez_sqlite_shim.so ]; then \
-	  echo "=== Rebuilding chez_sqlite_shim.so ==="; \
+# the case where vendor/jerboa-sqlite is updated but the .so isn't refreshed.
+sqlite-bundled:
+	@command -v cargo >/dev/null 2>&1 || { \
+	  echo "ERROR: cargo not found on PATH. Install rustup from rustup.rs"; exit 1; }
+	@if [ ! -f "$(SQLITE_BUNDLED_A)" ] || \
+	    [ "$(SQLITE_BUNDLED_DIR)/Cargo.toml" -nt "$(SQLITE_BUNDLED_A)" ] || \
+	    [ -n "$$(find $(SQLITE_BUNDLED_DIR)/src -name '*.rs' -newer $(SQLITE_BUNDLED_A) 2>/dev/null)" ]; then \
+	  echo "=== Building bundled SQLite archive ==="; \
+	  ( cd $(SQLITE_BUNDLED_DIR) && cargo build --release ); \
+	fi
+
+sqlite-shim: vendor/jerboa-sqlite sqlite-bundled
+	@if [ ! -f vendor/jerboa-sqlite/jerboa_sqlite_shim.so ] || \
+	    [ vendor/jerboa-sqlite/jerboa_sqlite_shim.c -nt vendor/jerboa-sqlite/jerboa_sqlite_shim.so ] || \
+	    [ support/sqlite3.h -nt vendor/jerboa-sqlite/jerboa_sqlite_shim.so ] || \
+	    [ "$(SQLITE_BUNDLED_A)" -nt vendor/jerboa-sqlite/jerboa_sqlite_shim.so ]; then \
+	  echo "=== Rebuilding jerboa_sqlite_shim.so ==="; \
 	  cc -shared -fPIC -O2 \
-	    -I$(SQLITE_LIB_DIR)/../include -L$(SQLITE_LIB_DIR) \
-	    -o vendor/chez-sqlite/chez_sqlite_shim.so \
-	    vendor/chez-sqlite/chez_sqlite_shim.c -lsqlite3; \
+	    -Isupport \
+	    -o vendor/jerboa-sqlite/jerboa_sqlite_shim.so \
+	    vendor/jerboa-sqlite/jerboa_sqlite_shim.c $(SQLITE_BUNDLED_A); \
 	fi
 
+sqlite-lib: vendor/jerboa-sqlite
+	@rm -rf $(SQLITE_STAGE)
+	@mkdir -p $(SQLITE_STAGE)
+	@sed '/^(import (jerboa prelude))$$/d' \
+	  vendor/jerboa-sqlite/src/jerboa-sqlite.ss > $(SQLITE_STAGE)/jerboa-sqlite.ss
+	$(JERBUILD) transpile $(SQLITE_STAGE) vendor/jerboa-sqlite/lib --force
+
 # ── libjerboa_native (Rust FFI) ─────────────────────────────────────────────
 # secrets.ss + std/net/request + std/regex etc. all bind FFI symbols from
 # libjerboa_native at library load time, so the shared lib must exist in
@@ -135,9 +150,12 @@ native-rs: $(NATIVE_DIR)
 	@command -v cargo >/dev/null 2>&1 || { \
 	  echo "ERROR: cargo not found on PATH. Install rustup from rustup.rs"; exit 1; }
 	@if [ ! -f "$(NATIVE_A)" ] || \
+	    [ ! -f "$(NATIVE_SENTINEL)" ] || \
 	    [ -n "$$(find $(NATIVE_DIR)/src -name '*.rs' -newer $(NATIVE_A) 2>/dev/null)" ]; then \
 	  echo "=== Building libjerboa_native (features: $(NATIVE_FEATURES)) ==="; \
-	  ( cd $(NATIVE_DIR) && cargo build --release --features $(NATIVE_FEATURES) ); \
+	  ( cd $(NATIVE_DIR) && cargo build --release --no-default-features --features $(NATIVE_FEATURES) ); \
+	  rm -f $(NATIVE_DIR)/target/release/.built-with-*; \
+	  touch "$(NATIVE_SENTINEL)"; \
 	fi
 	@mkdir -p lib
 	@# The dev loop loads a shared object; the binary links the .a (jerbuild
@@ -155,13 +173,13 @@ native-rs: $(NATIVE_DIR)
 # we do NOT use git submodules — every target that needs vendor/<x> depends on
 # vendor-deps so a fresh checkout populates itself.
 
-vendor-deps: vendor/chez-sqlite vendor/termbox2 vendor/jerboa-websearch
+vendor-deps: vendor/jerboa-sqlite vendor/termbox2 vendor/jerboa-websearch
 
-vendor/chez-sqlite:
+vendor/jerboa-sqlite:
 	@mkdir -p vendor
-	@echo "=== Cloning chez-sqlite into vendor/ ==="
-	@git clone --depth 1 git@git.sr.ht:~lisp/chez-sqlite vendor/chez-sqlite
-	@rm -f vendor/chez-sqlite/chez_sqlite_shim.so
+	@echo "=== Cloning jerboa-sqlite into vendor/ ==="
+	@git clone --depth 1 https://git.sr.ht/~lisp/jerboa-sqlite vendor/jerboa-sqlite
+	@rm -f vendor/jerboa-sqlite/jerboa_sqlite_shim.so
 
 vendor/termbox2:
 	@mkdir -p vendor
@@ -171,7 +189,7 @@ vendor/termbox2:
 vendor/jerboa-websearch:
 	@mkdir -p vendor
 	@echo "=== Cloning jerboa-websearch into vendor/ ==="
-	@git clone --depth 1 git@git.sr.ht:~lisp/jerboa-websearch vendor/jerboa-websearch
+	@git clone --depth 1 https://git.sr.ht/~lisp/jerboa-websearch vendor/jerboa-websearch
 
 # Sparse-clone just the jerboa-native-rs crate out of the jerboa monorepo.
 # Depended on only by native-rs (not vendor-deps), so cross targets — which
@@ -179,7 +197,7 @@ vendor/jerboa-websearch:
 $(NATIVE_DIR):
 	@mkdir -p vendor
 	@echo "=== Sparse-cloning jerboa-native-rs from the jerboa monorepo ==="
-	@git clone --depth 1 --filter=blob:none --sparse git@git.sr.ht:~lisp/jerboa vendor/.jerboa-monorepo
+	@git clone --depth 1 --filter=blob:none --sparse https://git.sr.ht/~lisp/jerboa vendor/.jerboa-monorepo
 	@git -C vendor/.jerboa-monorepo sparse-checkout set jerboa-native-rs
 	@mv vendor/.jerboa-monorepo/jerboa-native-rs $(NATIVE_DIR)
 	@rm -rf vendor/.jerboa-monorepo
@@ -187,12 +205,12 @@ $(NATIVE_DIR):
 vendor-clean:
 	rm -rf vendor
 
-gen: vendor-deps purge-stale sqlite-shim
+gen: vendor-deps purge-stale sqlite-shim sqlite-lib
 	$(JERBUILD) transpile src lib
 
 build: gen native-rs
 	DYLD_LIBRARY_PATH=$(LDPATH) LD_LIBRARY_PATH=$(LDPATH) \
-	$(JERBUILD) compile $(LIBDIRS) main.ss < /dev/null
+	$(JERBUILD) compile $(LIBDIRS) support/build-check.ss < /dev/null
 
 run: native-rs
 	DYLD_LIBRARY_PATH=$(LDPATH) LD_LIBRARY_PATH=$(LDPATH) \
@@ -232,18 +250,7 @@ test-providers: build
 # .a, and links it all. Per-OS link flags come from --os-libs.
 binary: gen native-rs
 	DYLD_LIBRARY_PATH=$(LDPATH) LD_LIBRARY_PATH=$(LDPATH) \
-	$(JERBUILD) build --os-libs "$(JCODE_OS_LIBS)"
-	cp vendor/termbox2/jcode_tui_shim.dylib ./jcode_tui_shim.dylib 2>/dev/null || true
-	cp vendor/termbox2/jcode_tui_shim.so ./jcode_tui_shim.so 2>/dev/null || true
-
-# Fallback: the hand-rolled WPO build. Requires a full ~/mine/jerboa checkout
-# with a locally-built Chez ($(SCHEME)) and `make -C $(JERBOA_HOME) native`
-# already run. Kept until `binary` is proven across hosts, then removed.
-binary-legacy: gen tui-shim
-	JERBOA_HOME=$(JERBOA_HOME) \
-	DYLD_LIBRARY_PATH=$(LDPATH) LD_LIBRARY_PATH=$(LDPATH) \
-	$(SCHEME) -q --libdirs $(JERBOA_HOME)/lib:./lib:vendor/chez-sqlite/src:vendor/jerboa-websearch/src \
-	--script build-binary.ss
+	$(JERBUILD) build --config .jerbuild --os-libs "$(JCODE_OS_LIBS)"
 	cp vendor/termbox2/jcode_tui_shim.dylib ./jcode_tui_shim.dylib 2>/dev/null || true
 	cp vendor/termbox2/jcode_tui_shim.so ./jcode_tui_shim.so 2>/dev/null || true
 
@@ -251,164 +258,78 @@ install: binary
 	mkdir -p $(HOME)/.local/bin
 	cp jcode $(HOME)/.local/bin/jcode
 	cp vendor/termbox2/jcode_tui_shim.dylib $(HOME)/.local/bin/jcode_tui_shim.dylib 2>/dev/null || true
-	cp vendor/chez-sqlite/chez_sqlite_shim.so $(PREFIX)/lib/chez_sqlite_shim.so 2>/dev/null || true
 	@if [ "$$(uname)" = "Darwin" ]; then \
 	  codesign --force --sign - $(HOME)/.local/bin/jcode; \
 	  [ -f $(HOME)/.local/bin/jcode_tui_shim.dylib ] && codesign --force --sign - $(HOME)/.local/bin/jcode_tui_shim.dylib || true; \
 	fi
 	@echo "Installed to ~/.local/bin/jcode"
 
-# `make linux` now defaults to the fast cross-compile path. Use `make
-# linux-docker` for the canonical Docker build (reproducible, slower).
+# `make linux` builds on a Linux amd64 host through installed jerbuild.
 linux: linux-amd64
-linux-docker: linux-check docker
 
-# ── Fast local drift check for the musl build path ─────────────────────────
-# Runs compile-program on main-binary.ss with the same flags the musl build
-# uses, but skips sed patching, the C compile, and the linker. Catches drift
-# (new imports, removed stdlib exports, broken except clauses) on the dev's
-# laptop in ~10 seconds, before burning 5+ minutes in Docker.
-#
-# `linux` and `linux-local` both depend on this target so they fail fast.
+# ─── Linux amd64 binary ─────────────────────────────────────────────────────
+# Native Linux build through installed jerbuild. Requires cc and cargo.
+
+linux-amd64:
+	@case "$$(uname -s)-$$(uname -m)" in \
+	  Linux-x86_64|Linux-amd64) ;; \
+	  *) echo "ERROR: linux-amd64 is a native jerbuild target; run it on Linux amd64" >&2; exit 1 ;; \
+	esac
+	@$(MAKE) jcode-linux-native
+
+linux-arm64:
+	@case "$$(uname -s)-$$(uname -m)" in \
+	  Linux-aarch64|Linux-arm64) ;; \
+	  *) echo "ERROR: linux-arm64 is a native jerbuild target; run it on Linux arm64/aarch64" >&2; exit 1 ;; \
+	esac
+	@$(MAKE) jcode-linux-arm64-native
+
+jcode-linux-native: gen $(NATIVE_DIR)
+	@case "$$(uname -s)-$$(uname -m)" in \
+	  Linux-x86_64|Linux-amd64) ;; \
+	  *) echo "ERROR: .jerbuild.linux-amd64 must run on Linux amd64" >&2; exit 1 ;; \
+	esac
+	@echo "=== Building native Linux amd64 jcode with jerbuild ==="
+	$(JERBUILD) build --config .jerbuild.linux-amd64
+	@ls -lh jcode-linux-amd64
+	@file jcode-linux-amd64
 
-linux-check: gen
-	@echo "=== Running linux-check (fast musl-build drift check) ==="
-	DYLD_LIBRARY_PATH=$(LDPATH) LD_LIBRARY_PATH=$(LDPATH) \
-	$(SCHEME) -q --libdirs "$(XC_LIBDIRS)" --script linux-check.ss
-
-# ── Docker build (canonical static binary, zero runtime deps) ───────────────
-# Use `make linux` to build in Docker (canonical, reproducible).
-# Use `make linux-local` to build directly on the host (requires
-# musl-gcc and a musl-built Chez at ~/chez-musl or JERBOA_MUSL_CHEZ_PREFIX).
-
-docker:
-	@echo "=== Building jcode-musl in Docker ==="
-	@# Pass the host's ~/mine/jerboa as an additional build context. The base
-	@# image's bundled jerboa lags reality; without this overlay the build
-	@# breaks every time jcode source references a jerboa module added since
-	@# the base image was last rebuilt (e.g. (std os errno) added 2026-05-13).
-	DOCKER_BUILDKIT=1 docker build \
-	  --platform linux/amd64 \
-	  --build-arg CACHE_BUST=$$(date +%s) \
-	  --build-context host-jerboa=$(JERBOA_HOME) \
-	  -t jcode-builder .
-	@id=$$(docker create --platform linux/amd64 jcode-builder) && \
-	docker cp $$id:/out/jcode-musl ./jcode-musl && \
-	docker rm $$id >/dev/null && \
-	chmod +x jcode-musl
-	@echo ""
-	@ls -lh jcode-musl
-	@file jcode-musl
-
-# ─── musl Static Binary ──────────────────────────────────────────────────────
-# Build a fully static jcode binary using musl libc.
-# Requires: musl-gcc, Chez Scheme built with musl (~/chez-musl),
-#           jerboa-native-rs built for x86_64-unknown-linux-musl.
-
-linux-local: linux-check
-	@echo "=== Building static jcode with musl ==="
-	JERBOA_HOME=$(JERBOA_HOME) ./build-jcode-musl.sh
-
-# jcode-musl routes through Docker by default. The native `linux-local`
-# path depends on the host's Chez (musl + glibc) staying in lock-step
-# with the source — this kept breaking every time the host scheme moved
-# or a stale ~/chez-musl was around. The Docker path uses jerboa21/jerboa
-# which ships pre-built host + musl Chez and the full Rust toolchain, so
-# the build is reproducible and survives host drift. Use `make linux-local`
-# if you specifically want a native build.
-jcode-musl: docker
-
-# ─── Cross-compile (macOS / FreeBSD → Linux x86_64 musl) ────────────────────
-# Build a static jcode binary for Linux x86_64 from a non-Linux host.
-# No Docker needed — uses Chez xpatch + musl-gcc cross toolchain.
-#
-# Requires:
-#   - $(JERBOA_HOME)/.chez-cross-ta6le   — cross-built Chez (make chez-cross in jerboa)
-#   - x86_64-linux-musl-gcc on PATH       (brew install FiloSottile/musl-cross/musl-cross)
-#   - rustup target x86_64-unknown-linux-musl  (rustup target add x86_64-unknown-linux-musl)
-#
-# Produces: jcode-linux-amd64
-
-XC_LIBDIRS = ./lib:vendor/chez-sqlite/src:vendor/jerboa-websearch/src:$(JH)/lib:$(JERBOA_HOME)/lib
-
-linux-amd64: gen
-	@command -v x86_64-linux-musl-gcc >/dev/null 2>&1 || { \
-	  echo "ERROR: x86_64-linux-musl-gcc not found on PATH."; \
-	  echo "Install with: brew install FiloSottile/musl-cross/musl-cross"; \
-	  exit 1; }
-	@test -d $(JERBOA_HOME)/.chez-cross-ta6le || { \
-	  echo "ERROR: cross-built Chez not found at $(JERBOA_HOME)/.chez-cross-ta6le"; \
-	  echo "Build it with: cd $(JERBOA_HOME) && make chez-cross"; \
-	  exit 1; }
-	@command -v cargo >/dev/null 2>&1 || { \
-	  echo "ERROR: cargo not found on PATH. Install rustup from rustup.rs"; \
-	  exit 1; }
-	JERBOA_HOME=$(JERBOA_HOME) TARGET_ARCH=amd64 $(SCHEME) -q --libdirs "$(XC_LIBDIRS)" --script build-jcode-cross.ss
-
-linux-arm64: gen
-	@command -v aarch64-linux-musl-gcc >/dev/null 2>&1 || { \
-	  echo "ERROR: aarch64-linux-musl-gcc not found on PATH."; \
-	  echo "Install with: brew install FiloSottile/musl-cross/musl-cross"; \
-	  exit 1; }
-	@test -d $(JERBOA_HOME)/.chez-cross-tarm64le || { \
-	  echo "ERROR: cross-built Chez not found at $(JERBOA_HOME)/.chez-cross-tarm64le"; \
-	  echo "Build it with: cd $(JERBOA_HOME) && make chez-cross-tarm64le"; \
-	  exit 1; }
-	@command -v cargo >/dev/null 2>&1 || { \
-	  echo "ERROR: cargo not found on PATH. Install rustup from rustup.rs"; \
-	  exit 1; }
-	JERBOA_HOME=$(JERBOA_HOME) TARGET_ARCH=arm64 $(SCHEME) -q --libdirs "$(XC_LIBDIRS)" --script build-jcode-cross.ss
-
-# Friendly alias — same target, more explicit name.
 jcode-linux-amd64: linux-amd64
 
-test-linux-amd64: linux-amd64
-	@echo "=== Smoke testing jcode-linux-amd64 under podman/qemu alpine ==="
-	@command -v podman >/dev/null 2>&1 || { \
-	  echo "ERROR: podman not found. Install with: brew install podman"; \
-	  exit 1; }
-	podman run --rm --platform linux/amd64 \
-	  -v "$(CURDIR):/work:ro" -w /work \
-	  alpine:3 ./jcode-linux-amd64 --version
-
-test-linux: test-linux-amd64
-
-# ─── FreeBSD amd64 binary (cross-build from macOS) ──────────────────────
-# Produces a dynamic x86_64 FreeBSD ELF (jcode-freebsd-amd64) from a macOS
-# host. Depends on the target host's libc.so.7 / libm.so / libthr.so /
-# libutil.so at runtime.
-#
-# Why dynamic and not static: FreeBSD libc uses symbol versioning
-# (wait4@FBSD_1.0, etc.) that libc.a + libc_nonshared.a from base.txz
-# cannot satisfy at link time. Dynamic linking is the standard FreeBSD
-# distribution model anyway.
-#
-# Prereqs (set up once):
-#   - cd $(JERBOA_HOME) && make binary    (builds .chez-cross-ta6fb/)
-#   - FreeBSD sysroot at $(JERBOA_HOME)/.freebsd-sysroot/amd64
-#   - $(FREEBSD_AMD64_CC)
-#   - rustup target add x86_64-unknown-freebsd (one-time)
-
-freebsd-amd64: gen
-	@command -v $(firstword $(FREEBSD_AMD64_CC)) >/dev/null 2>&1 || { \
-	  echo "ERROR: $(FREEBSD_AMD64_CC) not found or not executable" >&2; \
-	  echo "  See top of Makefile freebsd-amd64 target for setup notes." >&2; \
-	  exit 1; }
-	@test -d $(JERBOA_HOME)/.chez-cross-ta6fb || { \
-	  echo "ERROR: $(JERBOA_HOME)/.chez-cross-ta6fb not found" >&2; \
-	  echo "  cd $(JERBOA_HOME) && make binary" >&2; \
-	  exit 1; }
-	@command -v cargo >/dev/null 2>&1 || { \
-	  echo "ERROR: cargo not found on PATH. Install rustup from rustup.rs"; \
-	  exit 1; }
-	JERBOA_HOME=$(JERBOA_HOME) CROSS_CC="$(FREEBSD_AMD64_CC)" $(SCHEME) -q --libdirs "$(XC_LIBDIRS)" --script build-jcode-freebsd-cross.ss
+jcode-linux-arm64-native: gen $(NATIVE_DIR)
+	@case "$$(uname -s)-$$(uname -m)" in \
+	  Linux-aarch64|Linux-arm64) ;; \
+	  *) echo "ERROR: .jerbuild.linux-arm64 must run on Linux arm64/aarch64" >&2; exit 1 ;; \
+	esac
+	@echo "=== Building native Linux arm64 jcode with jerbuild ==="
+	$(JERBUILD) build --config .jerbuild.linux-arm64
+	@ls -lh jcode-linux-arm64
+	@file jcode-linux-arm64
+
+jcode-linux-arm64: linux-arm64
+
+# ─── FreeBSD amd64 binary ───────────────────────────────────────────────────
+# Native FreeBSD build through installed jerbuild. Requires cc and cargo.
+
+freebsd-amd64:
+	@case "$$(uname -s)-$$(uname -m)" in \
+	  FreeBSD-x86_64|FreeBSD-amd64) ;; \
+	  *) echo "ERROR: freebsd-amd64 is a native jerbuild target; run it on FreeBSD amd64" >&2; exit 1 ;; \
+	esac
+	@$(MAKE) jcode-freebsd-native
+
+jcode-freebsd-native: gen $(NATIVE_DIR)
+	@case "$$(uname -s)-$$(uname -m)" in \
+	  FreeBSD-x86_64|FreeBSD-amd64) ;; \
+	  *) echo "ERROR: .jerbuild.freebsd-amd64 must run on FreeBSD amd64" >&2; exit 1 ;; \
+	esac
+	@echo "=== Building native FreeBSD amd64 jcode with jerbuild ==="
+	$(JERBUILD) build --config .jerbuild.freebsd-amd64
+	@ls -lh jcode-freebsd-amd64
+	@file jcode-freebsd-amd64
 
-# Friendly aliases.
 jcode-freebsd-amd64: freebsd-amd64
 
-# `make freebsd` defaults to the cross-from-macOS path (matches `make linux`).
-# For a native-on-FreeBSD build (legacy build-binary.ss code path), run
-# `make build` from a FreeBSD host instead.
 freebsd: freebsd-amd64
 
 
@@ -416,7 +337,7 @@ clean:
 	find lib/jcode -name "*.sls" -delete 2>/dev/null; true
 	find . -name "*.so" -delete
 	find . -name "*.wpo" -delete
-	rm -f jcode jcode-musl jcode-musl.sha256
+	rm -f jcode jcode-linux-amd64 jcode-linux-arm64 jcode-freebsd-amd64
 	rm -f lib/libjerboa_native.dylib lib/libjerboa_native.so jcode_tui_shim.dylib jcode_tui_shim.so
 
 # ── Android APK (thin client, no embedded binary) ───────────────────────────
diff --git a/README.md b/README.md
index eed8357..506d4db 100644
--- a/README.md
+++ b/README.md
@@ -3,7 +3,7 @@
 **A portable AI coding agent written in [Jerboa](https://git.sr.ht/~lisp/jerboa) Scheme.**
 
 `jcode` is a terminal coding agent — think opencode / aider / Claude Code — that
-compiles to a single static binary with no runtime dependencies. It talks to 13
+compiles to a standalone binary without a Node or Python runtime. It talks to 13
 LLM providers (cloud and local), drives your editor through a real tool loop,
 and ships a reliability layer that makes even small self-hosted models call
 tools dependably.
@@ -24,7 +24,7 @@ tools dependably.
 
 ## Why it exists
 
-- **Portable.** One static binary. Runs on macOS, Linux (glibc + musl), FreeBSD,
+- **Portable.** One standalone binary. Runs on macOS, Linux, FreeBSD,
   and Android (via Termux). No Node, no Python, no ecosystem.
 - **Provider-agnostic.** Anthropic, OpenAI, Google, OpenRouter, DeepSeek, xAI,
   Groq, Mistral, Together, Cerebras, Perplexity — plus local **Ollama** and
@@ -38,9 +38,9 @@ tools dependably.
 
 ## Quickstart
 
-You need a built [jerboa](https://git.sr.ht/~lisp/jerboa) checkout (`JERBOA_HOME`)
-and a Rust toolchain. See **[docs/getting-started.md](docs/getting-started.md)**
-for the full setup.
+You need the installed Jerboa toolchain (`jerbuild` on `PATH`), a C compiler,
+and Rust/Cargo. See **[docs/getting-started.md](docs/getting-started.md)** for
+the full setup.
 
 ```bash
 git clone https://git.sr.ht/~lisp/jerboa-code && cd jerboa-code
@@ -72,7 +72,7 @@ See **[docs/](docs/)** for the full index.
 ## Build targets
 
 `make build` · `test` · `run` · `run-tui` · `binary` · `install` ·
-`linux` · `linux-arm64` · `linux-docker` · `freebsd` · `android`.
+`linux` · `linux-amd64` · `linux-arm64` · `freebsd` · `android`.
 Run `make help` for the complete list.
 
 ## Layout
diff --git a/build-binary.ss b/build-binary.ss
deleted file mode 100644
index c9fc60a..0000000
--- a/build-binary.ss
+++ /dev/null
@@ -1,640 +0,0 @@
-#!chezscheme
-;; Build a native jcode binary.
-;;
-;; Usage: cd jerboa-code && make binary
-;;
-;; Produces: ./jcode (single binary with embedded boot files + program)
-;;
-;; All boot files are embedded as C byte arrays via Sregister_boot_file_bytes.
-;; SQLite shim is statically linked; chez-sqlite uses dlopen(NULL) to resolve.
-
-(import (chezscheme))
-
-;; --- Helper: generate C header from binary file ---
-(define (file->c-header input-path output-path array-name size-name)
-  (let* ((port (open-file-input-port input-path))
-         (data (get-bytevector-all port))
-         (size (bytevector-length data)))
-    (close-port port)
-    (call-with-output-file output-path
-      (lambda (out)
-        (fprintf out "/* Auto-generated — do not edit */~n")
-        (fprintf out "static const unsigned char ~a[] = {~n" array-name)
-        (let loop ((i 0))
-          (when (< i size)
-            (when (= 0 (modulo i 16)) (fprintf out "  "))
-            (fprintf out "0x~2,'0x" (bytevector-u8-ref data i))
-            (when (< (+ i 1) size) (fprintf out ","))
-            (when (= 15 (modulo i 16)) (fprintf out "~n"))
-            (loop (+ i 1))))
-        (fprintf out "~n};~n")
-        (fprintf out "static const unsigned int ~a = ~a;~n" size-name size))
-      'replace)
-    (printf "  ~a: ~a bytes~n" output-path size)))
-
-;; --- Detect OS ---
-(define freebsd?
-  (let ((mt (symbol->string (machine-type))))
-    (or (string=? mt "ta6fb") (string=? mt "a6fb")
-        (string=? mt "tarm64fb") (string=? mt "arm64fb"))))
-
-(define (string-has-suffix? s suffix)
-  (let ((slen (string-length s))
-        (suflen (string-length suffix)))
-    (and (>= slen suflen)
-         (string=? suffix (substring s (- slen suflen) slen)))))
-
-(define macos?
-  (string-has-suffix? (symbol->string (machine-type)) "osx"))
-
-(define termux?
-  (and (getenv "PREFIX")
-       (let ((prefix (getenv "PREFIX")))
-         (and (string? prefix) (> (string-length prefix) 0)
-              (file-exists? (format "~a/bin/termux-info" prefix))))))
-
-;; --- Locate Chez install directory ---
-(define (find-csv-dir lib-dir mt)
-  (let ((csv-dir
-          (let lp ((dirs (guard (e (#t '())) (directory-list lib-dir))))
-            (cond
-              ((null? dirs) #f)
-              ((and (> (string-length (car dirs)) 3)
-                    (string=? "csv" (substring (car dirs) 0 3)))
-               (format "~a/~a/~a" lib-dir (car dirs) mt))
-              (else (lp (cdr dirs)))))))
-    (and csv-dir
-         (file-exists? (format "~a/main.o" csv-dir))
-         csv-dir)))
-
-(define chez-dir
-  (or (getenv "CHEZ_DIR")
-      (let ((mt (symbol->string (machine-type)))
-            (home (getenv "HOME"))
-            (jerboa-home (or (getenv "JERBOA_HOME")
-                             (format "~a/mine/jerboa" (getenv "HOME")))))
-        ;; Prefer the self-built Chez under $JERBOA_HOME/.chez (canonical
-        ;; since jerboa commit 3fe0459 — `make binary` there builds it).
-        (or (find-csv-dir (format "~a/.chez/lib" jerboa-home) mt)
-            (find-csv-dir (format "~a/.local/lib" home) mt)
-            (find-csv-dir "/usr/local/lib" mt)
-            (find-csv-dir "/usr/lib" mt)
-            (find-csv-dir "/opt/homebrew/lib" mt)
-            (let ((prefix (getenv "PREFIX")))
-              (and prefix (find-csv-dir (format "~a/lib" prefix) mt)))))))
-
-(unless chez-dir
-  (display "Error: Cannot find Chez install dir. Set CHEZ_DIR.\n")
-  (exit 1))
-
-(define home (getenv "HOME"))
-(define jerboa-dir
-  (or (getenv "JERBOA_HOME")
-      (format "~a/mine/jerboa" home)))
-
-(printf "Chez dir:   ~a~n" chez-dir)
-(printf "Jerboa dir: ~a~n" jerboa-dir)
-
-;; Add library search paths
-(library-directories
-  (append
-    (list (cons (format "~a/lib" jerboa-dir)
-                (format "~a/lib" jerboa-dir)))
-    (list (cons "lib" "lib"))
-    (list (cons "vendor/chez-sqlite/src" "vendor/chez-sqlite/src"))
-    (list (cons "vendor/jerboa-websearch/src" "vendor/jerboa-websearch/src"))
-    (list (cons "." "."))
-    (library-directories)))
-
-(define jcode-modules
-  '("lib/jcode/core/models"
-    "lib/jcode/core/config"
-    "lib/jcode/core/log"
-    "lib/jcode/core/errors"
-    "lib/jcode/core/hardware"
-    "lib/jcode/core/steps"
-    "lib/jcode/core/workflow"
-    "lib/jcode/core/session"
-    "lib/jcode/core/message"
-    "lib/jcode/core/secrets"
-    "lib/jcode/core/secrets-import"
-    "lib/jcode/core/escalation"
-    "lib/jcode/core/permissions"
-    "lib/jcode/core/mentions"
-    "lib/jcode/core/agents-md"
-    "lib/jcode/core/hooks"
-    "lib/jcode/core/compaction"
-    "lib/jcode/core/compaction-strategy"
-    "lib/jcode/core/sandbox"
-    "lib/jcode/core/repomap"
-    "lib/jcode/core/checkpoints"
-    "lib/jcode/core/expert"
-    "lib/jcode/core/agent"
-    "lib/jcode/core/plugin"
-    "lib/jcode/core/debug-repl"
-    "lib/jcode/core/skill"
-    "lib/jcode/core/builtin-skills"
-    "lib/jcode/guardrails/nudge"
-    "lib/jcode/guardrails/error-tracker"
-    "lib/jcode/guardrails/message-type"
-    "lib/jcode/guardrails/rescue"
-    "lib/jcode/guardrails/validator"
-    "lib/jcode/guardrails/respond"
-    "lib/jcode/guardrails/guardrails"
-    "lib/jcode/guardrails/step-enforcer"
-    "lib/jcode/core/workflow-runner"
-    "lib/jcode/core/verified"
-    "lib/jcode/core/best-of-k"
-    "lib/jcode/provider/sampling"
-    "lib/jcode/provider/provider"
-    "lib/jcode/proxy/convert"
-    "lib/jcode/proxy/handler"
-    "lib/jcode/core/slot-worker"
-    "lib/jcode/proxy/server"
-    "lib/jcode/core/verified-run"
-    "lib/jcode/eval/scenario"
-    "lib/jcode/eval/ablation"
-    "lib/jcode/eval/runner"
-    "lib/jcode/tool/registry"
-    "lib/jcode/tool/file"
-    "lib/jcode/tool/apply-patch"
-    "lib/jcode/tool/bash"
-    "lib/jcode/tool/task"
-    "lib/jcode/tool/repomap-tool"
-    "lib/jcode/tool/web"
-    "lib/jcode/tool/batch"
-    "lib/jcode/tool/git"
-    "lib/jcode/tool/external-llm"
-    "lib/jcode/tool/lsp"
-    "lib/jcode/mcp/client"
-    "lib/jcode/ui/tui-ffi"
-    "lib/jcode/ui/tui-theme"
-    "lib/jcode/ui/tui-keys"
-    "lib/jcode/ui/tui-markdown"
-    "lib/jcode/ui/tui-diff"
-    "lib/jcode/ui/tui-message"
-    "lib/jcode/ui/tui-status"
-    "lib/jcode/ui/tui-input"
-    "lib/jcode/ui/tui-memstats"
-    "lib/jcode/ui/tui-sidebar"
-    "lib/jcode/ui/tui-dialog"
-    "lib/jcode/ui/tui-toast"
-    "lib/jcode/ui/tui-syntax"
-    "lib/jcode/ui/tui"
-    "lib/jcode/ui/relay"
-    "lib/jcode/ui/connect"
-    "lib/jcode/ui/cli"))
-
-;; --- Step 0: Clean stale .so files so WPO can recompile everything ---
-;; Vendor .so/.wpo pairs must be deleted together: if only the .wpo is
-;; stale relative to its .so, compile-imported-libraries sees the .so as
-;; up-to-date and skips regeneration, leaving compile-whole-program to
-;; fail with "does not define expected compilation instance of library".
-(printf "~n[0/6] Cleaning stale .so/.wpo files...~n")
-(for-each (lambda (m)
-            (let ((so (format "~a.so" m))
-                  (wpo (format "~a.wpo" m)))
-              (when (file-exists? so) (delete-file so))
-              (when (file-exists? wpo) (delete-file wpo))))
-  jcode-modules)
-(system (format "find ~a/lib -name '*.wpo' -delete 2>/dev/null" jerboa-dir))
-(system "find vendor/jerboa-websearch/src -name '*.so' -delete 2>/dev/null")
-(system "find vendor/jerboa-websearch/src -name '*.wpo' -delete 2>/dev/null")
-
-;; --- Step 0.5: Patch chez-sqlite for static linking ---
-;; In the static binary, the shim is compiled into the binary itself.
-;; We patch chez-sqlite to use (load-shared-object "") which is dlopen(NULL),
-;; resolving symbols already linked into the process (like jerboa-shell does).
-(printf "[0.5/6] Patching chez-sqlite for static linking...~n")
-(define chez-sqlite-sls "vendor/chez-sqlite/src/chez-sqlite.sls")
-(define chez-sqlite-backup "vendor/chez-sqlite/src/chez-sqlite.sls.bak")
-(system (format "cp ~a ~a" chez-sqlite-sls chez-sqlite-backup))
-;; For static builds: sqlite3 and the shim are linked into the binary,
-;; so load-shared-object is unnecessary. Replace _l1/_l2 with no-ops.