Call typed split tree from Jerboa

ober

3d50adbbde3bfd18087a95c6ba9dc1d837957e07

diff --git a/Makefile b/Makefile
index d8d02e6..4731a9c 100644
--- a/Makefile
+++ b/Makefile
@@ -336,7 +336,10 @@ typed-split-tree-smoke:
 	  test -n "$$lib" || { echo "ERROR: generated typed Rust dynamic library not found" >&2; exit 1; }; \
 	  JERBOA_TYPED_RUST_LIB="$$lib" \
 	  TYPED_WRAPPER_FILE="build/typed/split-tree-smoke/sample_typed_split_tree.ss" \
-	  $(SCHEME) --libdirs $(LIBDIRS) --script tests/test-typed-split-tree-e2e.ss
+	  $(SCHEME) --libdirs $(LIBDIRS) --script tests/test-typed-split-tree-e2e.ss && \
+	  JERBOA_TYPED_RUST_LIB="$$lib" \
+	  TYPED_WRAPPER_FILE="build/typed/split-tree-smoke/sample_typed_split_tree.ss" \
+	  $(SCHEME) --libdirs $(LIBDIRS) --script tests/test-typed-split-tree-caller.ss
 
 typed-test: test-typed-parser test-typed-checker test-typed-rust test-typed-wrappers typecheck
 
diff --git a/docs/jerboa-to-rust.md b/docs/jerboa-to-rust.md
index a741eab..9246e31 100644
--- a/docs/jerboa-to-rust.md
+++ b/docs/jerboa-to-rust.md
@@ -566,7 +566,11 @@ Exports:
 - The split-tree smoke test now compares generated typed/Rust results against a
   small dynamic tagged-list reference implementation and rejects a split whose
   stored size disagrees with its recursive child sizes.
-- Next split-tree work: use the typed split-tree from a small Jerboa caller
+- The split-tree smoke target also runs a small Jerboa caller that loads the
+  generated wrapper, builds a nested split tree through the wrapper API,
+  summarizes it with prelude helpers, and drops the handles.
+- Next split-tree work: make wrapper generation importable without explicit
+  `load` in callers.
 
 Why:
 
diff --git a/docs/typed-jerboa.md b/docs/typed-jerboa.md
index 2604afd..04ae17a 100644
--- a/docs/typed-jerboa.md
+++ b/docs/typed-jerboa.md
@@ -184,7 +184,9 @@ Current landing:
   typed leaf/split constructors, recursive total-size, validity, and flatten
   traversals, parent lookup, remove-by-leaf-id, leaf accessors, dynamic wrapper
   checks, rejection of inconsistent stored split sizes, string returns, opaque
-  handles, and explicit handle drops.
+  handles, and explicit handle drops. The same target now also runs a small
+  Jerboa caller that loads the generated wrapper and uses the public wrapper
+  functions from ordinary `(jerboa prelude)` code.
 - `support/typed-rust.ss`, `make typed-rust`, and `make typed-build` generate a
   disposable Cargo crate under `build/typed/rust`; `typed-build` also writes
   wrappers under `build/typed/jerboa` and runs `cargo build` against the
@@ -864,7 +866,10 @@ Minimum excluded features:
   results with a small dynamic split-tree reference implementation. It also
   checks that invalid stored split sizes are rejected by the typed validity
   function.
-- Use it from a small part of Jerboa.
+- Use it from a small part of Jerboa. The first caller smoke test now lives in
+  `tests/test-typed-split-tree-caller.ss`; it loads the generated wrapper,
+  builds a nested tree, summarizes it with Jerboa prelude helpers, checks
+  invalid stored sizes, removes a nested leaf, and drops all generated handles.
 
 ### Milestone 5: Effects and Resources
 
diff --git a/tests/test-typed-split-tree-caller.ss b/tests/test-typed-split-tree-caller.ss
new file mode 100644
index 0000000..7a9ae25
--- /dev/null
+++ b/tests/test-typed-split-tree-caller.ss
@@ -0,0 +1,70 @@
+(import (jerboa prelude)
+        (only (chezscheme) exit getenv load))
+
+(def wrapper-file
+  (or (getenv "TYPED_WRAPPER_FILE")
+      "build/typed/split-tree-smoke/sample_typed_split_tree.ss"))
+
+(load wrapper-file)
+
+(def pass 0)
+(def fail 0)
+
+(def (check name ok?)
+  (if ok?
+    (begin
+      (set! pass (+ pass 1))
+      (displayln "  ok " name))
+    (begin
+      (set! fail (+ fail 1))
+      (displayln "FAIL " name))))
+
+(def (split-tree-summary tree)
+  (alist
+    (size (split-tree-size tree))
+    (total (split-tree-total-size tree))
+    (flat (split-tree-flatten tree))
+    (valid? (split-tree-valid? tree))))
+
+(def (without-leaf-text tree id)
+  (split-tree-flatten (split-tree-remove tree id)))
+
+(displayln "--- Typed split-tree Jerboa caller smoke ---")
+
+(let* ([left (make-leaf 21 "jer")]
+       [middle (make-leaf 22 "boa")]
+       [right (make-leaf 23 "!")]
+       [inner (make-split 24 left middle 6)]
+       [root (make-split 25 inner right 7)]
+       [bad-root (make-split 26 inner right 99)])
+  (let-alist (split-tree-summary root) (size total flat valid?)
+    (check "caller summary size"
+      (= size 7))
+    (check "caller summary total"
+      (= total 7))
+    (check "caller summary flatten"
+      (string=? flat "jerboa!"))
+    (check "caller summary valid"
+      valid?))
+  (check "caller nested parent"
+    (= (split-tree-find-parent root 21 0) 24))
+  (check "caller root parent sentinel"
+    (= (split-tree-find-parent root 25 0) 0))
+  (check "caller invalid split size"
+    (not (split-tree-valid? bad-root)))
+  (check "caller remove nested leaf"
+    (string=? (without-leaf-text root 22) "jer!"))
+  (check "caller typed predicate"
+    (split-tree? root))
+  (check "caller drop handles"
+    (and (%typed-rust-handle-drop! root)
+         (and (%typed-rust-handle-drop! bad-root)
+              (and (%typed-rust-handle-drop! inner)
+                   (and (%typed-rust-handle-drop! left)
+                        (and (%typed-rust-handle-drop! middle)
+                             (%typed-rust-handle-drop! right))))))))
+
+(displayln "")
+(displayln "Typed split-tree Jerboa caller smoke: " pass " passed, " fail " failed")
+(when (> fail 0)
+  (exit 1))