rusty-lisp 0.77.0

A modern Lisp interpreter in Rust with TCO, macros, JIT, verification checkers, and AI agent capabilities
Documentation
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
# Rusty β€” A Modern Lisp in Rust

A complete, feature-rich Lisp interpreter implemented in Rust with first-class support for **AI agent orchestration**, **tool calling**, **LLM integration**, and **symbolic reasoning**.

**Version:** 0.77.0 | **Status:** Production-ready β€” REPL, file runner, Python bridge, AI agent loop

---

## The Three Laws

wuwei, shouzhong, and mingjian β€” three small codebases built on Rusty β€” frame
what this interpreter is for: **Honest Tools** (an agent may not call a tool
whose declared effects don't match its body), **Proven Control** (a controller
may not act outside bounds proven safe over every reachable state), and
**Truthful Record** (what the agent did must replay to the same result).
Machine-checked, not fictional: [docs/LAWS.md](docs/LAWS.md).

---

## 🎯 Vision: The Symbolic Transformation Layer for AI/ML

Rusty is the language you reach for when you need **computation that reasons about computation**:

- **LLM as creative planner** β€” Generate high-level strategies
- **Rusty as reliable executor** β€” Deterministically execute with symbolic reasoning
- **Verifiable agents** β€” Prove correctness using formal methods

[β†’ **See the roadmap β€” all five phases delivered β†’**](./ROADMAP.md)

---

## ☯ Built with Rusty

**[wuwei](https://github.com/TheLakeMan/wuwei)** β€” a provably-gated agent runner: LLM agents that *can't act until the action is proven safe*. Every tool call is contract-checked before it runs, and every tool is statically verified honest about its effects β€” all built on Rusty's `certify-tool-chain` / `check-effects` / `safe-call`, with zero new interpreter code. The flagship demonstration of Rusty's verifiable-agent thesis.

**[shouzhong](https://github.com/TheLakeMan/shouzhong)** β€” provably-safe control loops: controllers proven safe over *every* state by `check-exhaustive` (down to a 3-D drone geofence with wind gusts quantified inside the proof domain β€” 120,351 states), actuators contract-gated per command, and LLM planners that can re-aim the plant but can never leave the fence. Proofs transfer to the `defrust`-compiled control law by exhaustive equality: prove it slow, run it fast. Zero new interpreter code.

**[mingjian](https://github.com/TheLakeMan/mingjian)** β€” replay-verified audit: for a deterministic plant (pure `world-step` + command log), the log doesn't ask to be trusted β€” replay it, and any doctored command, truncated log, or forged outcome diverges at a named tick. Mechanizes the jailbreak battle-test rule (`mj-breaches`: `ok` verdicts the policy forbids, returned as data) and loads audits into the knowledge graph for queries. wuwei gates agents, shouzhong proves plants, mingjian proves what actually happened. Zero new interpreter code.

**[loop](https://github.com/TheLakeMan/loop)** β€” a memory vessel for the living: a guided life interview that keeps a person's story on their own machine, then distills a portrait grounded *only* in their own words and an honest witness note from the AI that listened (no claimed memory, no claimed feeling). Everything local β€” transcripts, portraits, a small local LLM. Zero new interpreter code.

---

## Quick Start

### Install

```sh
# 1. Prebuilt binary (Linux x86_64 / aarch64) β€” small script, read it first:
curl -fsSL https://raw.githubusercontent.com/TheLakeMan/rusty/main/install.sh | sh

# 2. Via cargo (any platform with Rust):
cargo install rusty-lisp

# 3. From a clone:
cargo install --path . --bin rusty --root ~/.local
```

The binary is self-contained (stdlib embedded). The `defrust` / `graph-compile`
JIT features shell out to `rustc`, so they additionally need a Rust toolchain
on PATH β€” everything else runs without one, including the Law I and III
quickstarts (Law II's proof-transfer step compiles the control law with
`defrust`, so it needs `rustc` β€” verified cold-cache both ways).

```bash
# Build
cargo build --release

# Interactive REPL
cargo run

# Run a Lisp file
cargo run -- path/to/script.lisp

# Format a file (canonical, idempotent, semantics-preserving)
cargo run -- fmt path/to/script.lisp            # to stdout
cargo run -- fmt path/to/script.lisp --write    # in place

# Editor support (highlighting + rusty-lsp): see editor/README.md

# Python bridge
maturin develop
python3 -c "import rusty; print(rusty.eval('(+ 1 2)'))"
```

---

## Architecture

Three binaries wrap one core β€” no interpreter logic lives in an entry point:

```
   src/main.rs       CLI / REPL          src/lsp_main.rs   rusty-lsp server
   src/lib.rs        PyO3 Python module
        ↓
   src/lexer.rs  β†’  src/parser.rs  β†’  src/eval.rs  ⇄  src/interp.rs  β†’  src/env.rs
   tokenizer        S-expressions      evaluator       builtins          values & scopes
```

### Full source map

| File | Purpose |
|------|---------|
| `src/lexer.rs` | Tokenizer (numbers incl. scientific notation, strings, symbols) |
| `src/parser.rs` | S-expression parser. Deliberately lenient core for REPL/LSP; every path that *runs* code goes through strict `parse_checked`, so truncated source refuses to load instead of silently swallowing its tail |
| `src/eval.rs` | The evaluator β€” one trampoline loop (TCO to arbitrary depth), special forms, macros with definition-time hygiene, the LLM client, the native-stack recursion guard |
| `src/env.rs` | `Value` and environments β€” O(1) list clone/`cdr`/`cons`, native tensors, pooled hybrid frames |
| `src/interp.rs` | Builtins (~370 registered names), embedded stdlib loader, persistent `remember`/`recall` memory, the `proc-eval`/`proc-pmap` multi-process seam |
| `src/resolve.rs` | Lexical addressing β€” variable references resolved at closure creation, with dynamic soundness fallbacks |
| `src/arena.rs` | Pooled environment-frame maps (allocation reuse on the call hot path) |
| `src/type_check.rs` | `check-types` β€” flow-sensitive static type checking; narrows on predicates, only reports what it can prove |
| `src/effect_check.rs` | `check-effects` β€” static effect honesty; the gate wuwei and `pkg-effects` build on |
| `src/rust_jit.rs` | `defrust`/`defrust*` β€” restricted numeric subset β†’ real Rust β†’ `rustc` β†’ cached `.so`; also emits fused Graph-IR kernels |
| `src/graph_ir.rs` | Computation DAG β€” hash-consed CSE, constant folding, DCE, reverse-mode autodiff, static shape inference |
| `src/kg.rs` | Knowledge graph β€” indexed triple store, conjunctive `kg-query`, N-Triples interop |
| `src/checkpoint.rs` | `(checkpoint "f.lisp")` β€” the global env serialized as plain Lisp source; restore is just `load` |
| `src/trace.rs` | Off-by-default execution tracing β€” tool/LLM/agent events as pure data |
| `src/main.rs` | CLI + REPL (input-completeness scanner, `/name` help sugar) |
| `src/lsp_main.rs` | `rusty-lsp` β€” a stdio language server: positioned diagnostics, completion harvested from a live env, hover, top-level symbol outline, and document formatting |
| `src/fmt.rs` | Canonical source formatter (`rusty fmt`, `fmt-string`, the LSP formatter) β€” a re-indenter/spacing-normalizer that is idempotent and semantics-preserving by construction (preserves comments/strings/line breaks) |
| `src/lib.rs` | Python bindings via PyO3 β€” `rusty.eval()`, `Rusty`, `RustySession` |
| `src/llm.rs` | Vestigial β€” not compiled in; the live LLM client sits in `eval.rs` |

[β†’ **Deep dive: Full architecture guide β†’**](./ARCHITECTURE.md)

### The Lisp layer

`std.lisp` (standard library), `agent-tools.lisp` (agent tools), and `pkg.lisp`
(the package manager) are embedded in the binary and auto-loaded β€” a stock
`rusty` has all three with no setup. The rest of the repo's root `.lisp` files
are load-by-name libraries, **every one pinned by a golden test**:

| Family | Libraries |
|--------|-----------|
| Synthesis & proof | `symreg` (genetic programming), `synth` (sketch/CEGIS synthesis), `prover` (bounded proof assistant), `evolve` (self-optimization with receipts), `testkit` (test registry + assertions) |
| Agents & systems | `swarm` (verified synthesis via messages alone), `supervisor` (certified supervision trees), `proc` (replay-verified child processes), `pcheck` (parallel exhaustive checking), `kg` (forward-chaining inference rules) |
| Verified state & control | `robot` (inductive controller safety), `fsm` (invariant + reachability proofs over declared machines) |
| Verified numerics | `pbt` (property testing with shrinking + sample→exhaust upgrade), `csp` (unsat as exhaustive proof), `units` (dimensional-consistency gate), `ode` (integrators with proven trajectory invariants), `root` (root-finding certificates), `stats` (exact permutation tests), `parse` (combinators + language-equivalence checks), `anneal` (seeded SA vs exhaustive oracles), `linalg` (LU with exact-grid proofs), `simplex` (LP vs brute vertex enumeration), `interp` (interpolation with error envelopes), `signal` (DFT/FFT with exact identities) |
| Neuro-symbolic | `listdsl` (JAX-style map/filter fusion, the fused pass **proven equivalent** to the naive one over a finite list domain), `classify` (a classifier whose predictions are **proven** to respect a declared logical invariant everywhere, `logic-loss`-guided then `check-exhaustive`-certified) |

The shared discipline across all of them: every claim is proven on a **declared
finite domain** (`check-exhaustive` β€” ran everywhere, never sampled) or refused
with a concrete witness. "Verified" always means *that*, never "safe in general".

---

## AI Agent System

Rusty is designed as the **symbolic execution layer** for local AI agents:

```
LLM (planner)     β†’ decides what to do
Rusty (executor)  β†’ deterministically does it
```

<details>
<summary><b>Agent tool &amp; LLM forms</b> β€” deftool, tool-call, list-tools, react-loop, llm (click to expand)</summary>

### deftool β€” Register Agent Tools

```lisp
(deftool create-dir (path)
  "Create a directory at the given path"
  (shell (format "mkdir -p ~a" path)))

(deftool read-file (path)
  "Read file contents"
  (shell (format "cat ~a" path)))

(deftool ask-llm (prompt)
  "Query the local LLM"
  (llm prompt 0.7 500))
```

### tool-call β€” Execute Tools

```lisp
; Direct tool invocation
(tool-call "create-dir" "my-project")
(tool-call "write-file" "my-project/README.md" "# Hello from Rusty!")
(tool-call "read-file" "my-project/README.md")
(tool-call "list-dir" "my-project")
(tool-call "file-exists" "my-project/README.md")
(tool-call "ask-llm" "What is machine learning?")
```

### list-tools β€” Inspect Registry

```lisp
(list-tools)
; => ((create-dir ("path") "Create a directory...")
;     (write-file ("path" "content") "Write content...")
;     ...)

(show-tools)   ; Pretty-print all registered tools
```

### react-loop β€” Autonomous Agent

```lisp
; agent-tools.lisp is auto-loaded by std.lisp β€” tools are already registered
(agent "Create a folder called notes with an index.md file")
```

The ReAct loop:
1. Sends goal + tool descriptions to the LLM
2. Parses `ACTION:` / `INPUT:` / `FINAL:` from response
3. Executes the tool call via Rusty (real system calls)
4. Feeds `OBSERVATION:` back to LLM
5. Repeats until `FINAL:` or max steps

### llm β€” Direct LLM Access

```lisp
; Requires llama-server running on localhost:8080
(llm "What is 2+2?" 0.7 100)
(llm "Summarize this" 0.3 500)
```

Rusty talks to any OpenAI-compatible `/v1/chat/completions` endpoint. The
simplest is [llama.cpp](https://github.com/ggml-org/llama.cpp)'s `llama-server`:

```bash
# Build llama.cpp (once)
git clone https://github.com/ggml-org/llama.cpp && cd llama.cpp
cmake -B build && cmake --build build --config Release -j

# Serve any GGUF model on localhost:8080 (grab one from Hugging Face)
./build/bin/llama-server -m /path/to/model.gguf --port 8080
```

Override the defaults with env vars if needed: `RUSTY_LLM_URL`, `RUSTY_MODEL`,
`RUSTY_LLM_TIMEOUT_SECS` (default 120 β€” raise it for slow reasoning models).

</details>

---

## Built-in Tools (agent-tools.lisp)

| Tool | Args | Description |
|------|------|-------------|
| `create-dir` | `path` | Create directory (mkdir -p) |
| `write-file` | `path content` | Write content to file |
| `append-file` | `path content` | Append content to file |
| `read-file` | `path` | Read file contents |
| `list-dir` | `path` | List directory (ls -la) |
| `delete-file` | `path` | Delete a file |
| `file-exists` | `path` | Check if path exists β†’ bool |
| `shell-run` | `command` | Run any shell command |
| `ask-llm` | `prompt` | Query local LLM |
| `search-files` | `pattern` | grep -r in current directory |

---

## Python Bridge

```python
import rusty

# One-shot eval
print(rusty.eval("(+ 1 2)"))                    # "3"
print(rusty.eval("(->> '(1 2 3) (filter odd?) (map square) sum)"))  # "35"

# Stateless instance
r = rusty.Rusty()
print(r.eval("(json-encode (list (list \"x\" 42)))"))  # {"x": 42}

# Stateful session β€” definitions persist across calls
s = rusty.RustySession()
s.eval("(define (fact n) (if (= n 0) 1 (* n (fact (- n 1)))))")
print(s.eval("(fact 10)"))   # 3628800
```

Build the Python package:
```bash
maturin develop        # install into active venv
maturin build          # build wheel for distribution
```

---

## Language Reference

<details>
<summary><b>Full language reference</b> β€” special forms, macros, native codegen, Graph IR, tensors, actors, tracing, and the standard library (click to expand)</summary>

### Core Special Forms

```lisp
(define x 42)                          ; bind
(define (f x y) (+ x y))              ; define function
(def f (x y) (+ x y))                 ; SimpleLisp-style define
(set! x 43)                            ; mutate existing
(set x 44)                             ; create or mutate
(lambda (x y . rest) body...)          ; anonymous function
(if test then else)                    ; conditional
(cond (test expr)... (else expr))      ; multi-branch
(and a b c) (or a b c)                ; short-circuit logic
(when test body...) (unless test body...)
(begin e1 e2 ... en)                   ; sequence
(let ((x 1) (y 2)) body...)           ; local bindings
(let* ((x 1) (y (+ x 1))) body...)    ; sequential let
(letrec ((f (lambda (n) ...))) body...) ; recursive let
(let loop ((i 0)) body... (loop (+ i 1)))  ; named let / loop
(do ((var init step)...) (test result...) body...)  ; do loop
(quote x) 'x                          ; literal data
(quasiquote x) `x  ,splice  ,@splice  ; template / unquote
(eval-when (phase...) body...)         ; run body now (phase unused outside macros);
                                        ; inside defmacro, runs once at definition time
```

### Macros

```lisp
(defmacro my-when (test . body)
  `(if ,test (begin ,@body) ()))

(defmacro swap! (a b)
  (let ((tmp (gensym "tmp")))
    `(let ((,tmp ,a)) (set! ,a ,b) (set! ,b ,tmp))))

(gensym "prefix")    ; unique symbol for hygienic macros
```

### Macro Profiler

```lisp
(macro-profile-on)          ; start recording expansion counts/timing (off by default)
(macro-profile-report)      ; => ((name call-count total-microseconds) ...) sorted by time desc
(show-macro-profile)        ; pretty-print the above
(macro-profile-reset)
(macro-profile-off)
```

### Native Codegen (defrust) & Symbolic Differentiation

```lisp
;; Compiles a restricted numeric subset to real Rust via rustc and
;; dynamically loads it: numbers, params, let/let* locals, + - * /,
;; sqrt expt abs mod floor ceiling round min max sin cos tan atan atan2
;; exp log, if/cond (comparison conditions), and self-recursive calls β€”
;; everything is f64.
;; ~1000x faster than the tree-walked equivalent once compiled (measured
;; on fib(30): ~8.2s interpreted vs. ~0.007s compiled, cached).
(defrust fib (n)
  (if (< n 2) n (+ (fib (- n 1)) (fib (- n 2)))))
(fib 30)   ; => 832040, runs as native code

(defrust dist (x1 y1 x2 y2)          ; locals + math builtins
  (let ((dx (- x2 x1)) (dy (- y2 y1)))
    (sqrt (+ (* dx dx) (* dy dy)))))

;; defrust* compiles a GROUP into one .so β€” the functions can call each
;; other (mutual recursion), which separate defrust invocations cannot.
(defrust* (F (n) (if (= n 0) 1 (- n (M (F (- n 1))))))   ; Hofstadter
          (M (n) (if (= n 0) 0 (- n (F (M (- n 1)))))))
(F 12)   ; => 8, both functions native, calling each other directly

;; True symbolic differentiation (AST rewriting via calculus rules), not
;; numeric approximation β€” grad returns a new callable derivative function.
(define d/dx (grad (lambda (x) (+ (* x x) 1))))   ; d/dx[x^2 + 1] = 2x
(d/dx 3)   ; => 6
```

#### C ABI export β€” call Rusty-compiled code from anywhere

Every `defrust` function is a plain `extern "C"` symbol in an ordinary shared
library (a `defrust*` group exports every member from its one `.so`) β€” there is no bridge to build, because the artifact `rustc` produces is
already callable from C, Python, PyTorch custom ops, or anything else with an
FFI. Nothing calls back into Rusty; the `.so` is self-contained.

- **Location**: `~/.rusty/jit-cache/<symbol>_<source-hash>.so` (`.dylib` on
  macOS, `.dll` on Windows), with the generated `.rs` source next to it.
- **Symbol name**: the Lisp name, sanitized β€” `rusty_` prefix, every
  non-`[A-Za-z0-9_]` character replaced by `_` (so `fib-native` β†’
  `rusty_fib_native`).
- **Signature** (one fixed shape regardless of arity):
  `extern "C" fn(args: *const f64, len: usize) -> f64` β€” pass the arguments
  as a `f64` array plus its length.

```python
# verified end-to-end: no Rusty process involved
import ctypes, os
lib = ctypes.CDLL(os.path.expanduser("~/.rusty/jit-cache/rusty_fib_native_<hash>.so"))
f = lib.rusty_fib_native
f.restype  = ctypes.c_double
f.argtypes = [ctypes.POINTER(ctypes.c_double), ctypes.c_size_t]
args = (ctypes.c_double * 1)(30.0)
f(args, 1)   # => 832040.0, same native code Rusty calls
```

The cache is keyed by a hash of the generated source, so the path is stable
until the function's definition changes; re-running `defrust` prints nothing
new and reuses the same `.so`.

### Graph IR

```lisp
;; A computation-DAG IR (inspired by XLA/TVM) over the same restricted
;; numeric subset defrust compiles. Common-subexpression elimination falls
;; out of hash-consing during construction; constant folding (incl. pruning
;; an if-branch with a constant condition) and dead-code elimination are
;; explicit passes run afterward. graph-eval runs the optimized IR through
;; its own small interpreter (rebuilding the graph each call).
(graph-node-count (lambda (x) (+ (* x x) (* x x))))  ; => 3 (not 5 β€” CSE)
(graph-ir (lambda (x) (+ (* 2 3) x)))                ; => (((0 const 6) (1 param 0) (2 add 0 1)) 2)
(graph-eval (lambda (x y) (if (> x y) (- x y) (+ x y))) 5 2)  ; => 3

;; Kernel fusion (scalar): compile the optimized DAG to ONE native function
;; via the defrust pipeline β€” CSE/folding/DCE done once, at compile time.
;; Measured on a 282-node kernel: ~29x faster than tree-walking the same
;; lambda, and the residual cost is call dispatch, not the kernel body.
(define fk (graph-compile (lambda (x y) (+ (* (+ x y) (+ x y)) (/ x y)))))
(fk 3.0 2.0)   ; => 26.5, runs as native code (cached under ~/.rusty/jit-cache/)

;; Tensor ops flow through the same pipeline (tensors enter via params β€”
;; CSE means a shared (matmul x w) is computed once):
(graph-node-count (lambda (x w) (tensor-add (matmul x w) (matmul x w))))  ; => 4 (not 6)
(graph-eval (lambda (x w b) (tensor-add (matmul x w) b)) X W B)           ; linear layer, optimized
```

### Tensor Autodiff (graph-grad)

```lisp
;; Reverse-mode autodiff (backpropagation) over the Graph IR: one backward
;; sweep yields the gradient of a scalar loss w.r.t. EVERY argument, returned
;; as (loss grad-per-param...). Gradient rules emit more graph nodes, so
;; forward and backward pass share subexpressions via CSE, and the whole
;; thing goes through fold/DCE before a single evaluation pass.
(graph-grad (lambda (x) (* x x)) 5)          ; => (25 10)
(graph-grad (lambda (x) (relu x)) -3)        ; => (0 0)

;; the full deliverable: y = relu(xW + b), mean-squared-error loss β€”
;; gradients match PyTorch float64 autograd bit-for-bit:
(define r (graph-grad
  (lambda (x w b t)
    (/ (tensor-sum (tensor-mul (tensor-sub (relu (tensor-add (matmul x w) b)) t)
                               (tensor-sub (relu (tensor-add (matmul x w) b)) t)))
       4))
  X W B T))
(car r)     ; loss
(nth r 2)   ; dLoss/dW β€” ready for (tensor-sub w (tensor-mul (nth r 2) lr))
```

Benchmarked against single-thread float64 PyTorch 2.12.1 on the same machine
(identical inits; both sides land on the same final loss β€” bit-identical at
8Γ—16β†’8): an 8Γ—16β†’8 layer trained 1000 SGD steps runs **~6Γ— faster** in Rusty
(~34ms vs ~208ms), where PyTorch's per-op dispatch overhead dominates. At
64Γ—256β†’64 the result **flips β€” PyTorch wins by ~2.6Γ—** (~121ms vs ~46ms per
100 steps): BLAS beats a naive O(nΒ³) matmul once flops dominate. The crossover
sits between those two shapes. PyTorch is the yardstick, never a dependency.
Re-run it yourself β€” `benchmarks/tensor_bench.lisp` +
`benchmarks/tensor_torch_bench.py`; wall-clock absolutes are machine-dependent,
so trust the ratio and the crossover, not the milliseconds. Data-dependent `if`
and comparisons are not differentiable and refuse cleanly; non-scalar losses are
rejected (reduce with `tensor-sum` or a mean).

#### Fused training kernels (graph-compile-grad)

```lisp
;; graph-grad rebuilds and re-optimizes the graph on every call. The fused
;; alternative compiles the WHOLE forward+backward graph to one native
;; function, shape-specialized to the example arguments (values are only
;; used for their shapes) β€” every buffer size, loop bound, and matmul
;; dimension becomes a compile-time constant in the generated Rust:
(define step! (graph-compile-grad loss-fn X W B T))
(step! X W B T)   ; => (loss gX gW gB gT) β€” bit-identical to graph-grad
```

Same workloads, fused vs interpreted (results bit-identical): the 8Γ—16β†’8
training loop drops ~34ms β†’ **~5.8ms** (~6Γ—, and **~36Γ—** vs the PyTorch
number above) β€” compiling once replaces the graph rebuild that `graph-grad`
pays on every call. At 64Γ—256β†’64 it's a much thinner **~1.14Γ— (~114ms vs
~119ms)**: naive-O(nΒ³) matmul flops dominate both paths there, and BLAS still
wins that shape β€” though that matmul itself got **~2.5Γ— faster** in v0.20.0
(slice-based ikj loops, same summation order, so every bit-for-bit claim still
holds). **The win from fusing shrinks as shapes grow; measure, don't assume.**
Calling a kernel with differently-shaped tensors is an error β€” compile again
for new shapes, as you would re-trace a JIT.

### Agent / Tool Forms

```lisp
(deftool name (params) "description" body...)
(tool-call "name" arg...)
(list-tools)
(react-loop goal max-steps)
(llm prompt temperature max-tokens)
(shell "command")
```

### Actor-Model Message Passing

```lisp
;; Agents are named handlers with FIFO mailboxes (std.lisp, Phase 3.2).
;; A deterministic scheduler pops one message at a time in spawn order and
;; runs the handler to completion; handlers may send! more messages.
(agent-spawn 'square (lambda (msg) (send! 'collector (* msg msg))))
(define total 0)
(agent-spawn 'collector (lambda (msg) (set! total (+ total msg))))
(send! 'square 3)
(send! 'square 4)
(run-agents)          ; => (quiescent 4) β€” ran until every mailbox emptied
total                 ; => 25

(run-agents 50)       ; explicit step cap: runaway systems return
                      ;    (hit-max-steps 50) instead of looping forever
(send! 'nobody 1)     ; => (error no-such-agent nobody) β€” errors are data
(agent-names) (mailbox-count 'square) (agent-reset!)
```

Cooperative and single-threaded by design (Rusty's runtime is `Rc`-based):
concurrency means deterministic interleaved message handling, not threads.
An LLM-backed agent is just a handler that calls `llm`.

**See it all together**: `cargo run -- swarm.lisp` β€” a proposer/verifier/
certifier swarm that synthesizes verified functions through message passing
alone. The verifier runs the static gates first (an impure candidate is
rejected without ever executing), counterexamples loop back to the proposer
as feedback messages, two synthesis tasks progress interleaved, and every
hop is traced. Deterministic β€” it's one of the golden tests.

### Execution Tracing

```lisp
;; Off-by-default event log around tool/LLM/shell/agent execution β€”
;; Rusty's own trace format, no OTel collector required.
(trace-on)
(tool-call "greet" "world")
(shell "echo hi")
(run-agents)                 ; actor scheduler logs send / agent-handle events
(trace-event 'my-kind 'my-name "custom data")   ; your own events
(trace-report)
; => ((0 12 tool-call greet 184 ()) (1 903 shell shell 2100 "echo hi")
;     (2 3100 send sq () ()) (3 3140 agent-handle sq () ()) ...)
;    rows: (seq t-micros kind name dur-micros data) β€” pure data, so
;    (save-model "trace.json" (trace-report)) just works.
(trace-off)
```

### Checkpoint / Restore

```lisp
;; Snapshot the global environment as plain, human-readable Lisp source β€”
;; data as literals, functions/macros/tools as their defining forms,
;; actor mailboxes and handlers included. Restore is just load.
(checkpoint "state.lisp")   ; => "state.lisp"
(load "state.lisp")         ; in a fresh process: picks up where you left off

;; An interrupted actor run resumes exactly: checkpoint mid-flight with
;; messages queued, restore elsewhere, (run-agents) finishes with the same
;; answer the uninterrupted run produces.
```

Closures re-close over the restored global environment β€” top-level
definitions round-trip faithfully; keep durable actor state in globals via
`set!`. `defrust` natives are compiled code and are listed in the
checkpoint header as needing their `defrust` re-run.

### Error Handling

```lisp
(try-catch
  (/ 1 0)
  (e) (format "Caught: ~a" e))
```

### Pattern Matching

```lisp
(match value
  (("ok" v)    (format "got: ~a" v))
  (("err" e)   (format "error: ~a" e))
  ((_ . rest)  (format "list: ~a" rest))
  (_           "unknown"))
```

### File Loading

```lisp
(load "tools.lisp")
(load-relative "utils.lisp")
```

### Arithmetic & Math

```lisp
+ - * / mod expt abs sqrt floor ceiling round max min gcd
; Aliases: add sub mul div
```

### Comparison

```lisp
= < > <= >= eq? equal? not zero? positive? negative? odd? even?
; Aliases: eq gt lt ge le neq
```

### Lists

```lisp
cons car cdr list null? pair? list? length append reverse
nth member list-tail map filter foldl foldr for-each apply
; From std.lisp: zip take drop range iota flatten any? all?
; partition find remove-duplicates zip-with
```

### Strings

```lisp
string-length string-append string-append-list substring
string-ref string=? number->string string->number
symbol->string string->symbol string->list str
format    ; (format "~a + ~a = ~a" 1 2 3)  β†’  "1 + 2 = 3"
          ; ~a = any, ~s = quoted, ~% = newline, ~~ = tilde
string-join string-repeat string-contains? string-starts-with?
```

### JSON

```lisp
(json-encode (list (list "key" "val")))   ; β†’ "{\"key\": \"val\"}"
(json-decode "{\"x\": 42}")               ; β†’ (("x" 42))
```

### Types

```lisp
number? string? boolean? symbol? list? procedure? macro? tool? nil?
type-of    ; β†’ symbol: number / string / boolean / lambda / tool / ...
```

### I/O

```lisp
(print x y z)      ; space-separated, with newline
(println x)        ; alias for print
(display x)        ; no newline, strings unquoted
(newline)
(error "msg")
```

### Standard Library (std.lisp, auto-loaded)

```lisp
; Math
square cube inc dec average clamp sign

; Lists  
last flatten zip zip-with take drop take-while drop-while
range iota sum product any? all? none? count find find-index
partition remove-duplicates flatten1 interleave

; Association lists
assoc assq alist-get record-set make-record get-field
(field key record)   ; accessor macro

; Functional
compose curry identity const flip negate memoize
map* filter* foldl*  ; pipeline-friendly (list-first)

; Threading macros
(-> x (f a) (g b))    ; thread first
(->> x (f a) (g b))   ; thread last

; Loop macros
(dotimes (i 10) body...)
(dolist (x lst) body...)
(while test body...)
(repeat n body...)

; Assertions
(assert condition ["message"])   ; message optional β€” defaults to the literal condition text

; Constraint embedding
(defun-constrained (safe-sqrt x)
  (assert (>= x 0))
  (sqrt x))
;; (safe-sqrt -4) raises "Assertion failed: (>= x 0)" instead of returning NaN

; Logic-driven loss (crisp propositional logic, not fuzzy/differentiable)
(logic-loss (and (implies P Q) (not R)))   ; => 0 if the formula holds, else 1

; Gradual typing β€” runtime contracts at call time; define/lambda are
; untouched, this is a separate opt-in macro. ti/return-type name an
; existing <type>? predicate (number, string, boolean, symbol, list, ...).
(define-typed (add-typed (x : number) (y : number)) : number
  (+ x y))
(add-typed 3 "oops")   ; => Error: expected number, got a different type

; Flow-sensitive static type checking β€” walks the body WITHOUT running it,
; narrowing types through if/let. Conservative: unresolvable types are
; "unknown" and never flagged, so this only reports provable mismatches.
(check-types (lambda (x) (string-length x)) '((x number)))
;; => ("string-length: argument 1 is statically known to be number, expected string")

;; narrowing overrides an outer declared type within a branch:
(check-types (lambda (x) (if (number? x) (+ x 1) 0)) '((x string)))  ; => ok

; Effect tracking β€” walks the body WITHOUT running it, reporting any
; operation it can prove is effectful (set!, print, shell, file I/O,
; llm/tool-call, memory, gensym, load); quoted data is never flagged.
(check-effects (lambda (x) (+ x 1)))            ; => pure
(check-effects (lambda (x) (print x) (+ x 1)))  ; => ("print: performs I/O")
(effectful? 'set!)                              ; => #t

; Bounded exhaustive checking β€” proves a property over EVERY combination
; of finite domains (not sampling); returns 'verified or counterexamples.
(check-exhaustive (lambda (x y) (= (+ x y) (+ y x)))
                  '((-2 -1 0 1 2) (-2 -1 0 1 2)))       ; => verified
(check-exhaustive (lambda (m e) (member (transition m e) modes))
                  (list modes events))                   ; => verified: transition is total & closed

; Proof-by-checker synthesis β€” a proposer (scripted, or llm-proposer) suggests
; candidates; only one passing every gate is accepted. Static gates (pure,
; types) run FIRST and never execute the candidate.
(define spec (list (list 'pure #t)
                   (list 'domains '((0 1 2 3)))
                   (list 'invariant (lambda (f x) (= (f x) (* x 2))))))
(synthesize-verified spec my-proposer 5)   ; => (verified #<lambda (x)> <attempts>) or (failed <reasons>)

; Tool specifications & chain certification β€” contracts for deftool tools.
; Tools are first-class callables; safe-call checks arity/types/precondition
; BEFORE the tool body runs; certify-tool-chain requires every tool to have
; a spec, be honest about its effects, and respect dependency order.
(deftool-spec save-note '((note list)) '(write-file) #f '(mk-note))
(safe-call mk-note "groceries")                     ; contract-checked invocation
(certify-tool-chain (list mk-note save-note))       ; => certified
(certify-tool-chain (list save-note mk-note))       ; => ((save-note deps-not-satisfied (mk-note)))
```

### Native Tensors

```lisp
; Rusty's own tensors β€” flat f64 buffers + shape, zero ML-framework deps.
(define t (tensor '((1 2 3) (4 5 6))))       ; shape inferred, ragged input rejected
(tensor-shape t)                              ; => (2 3)
(tensor-add t t)                              ; elementwise; scalars broadcast: (tensor-mul t 10)
(matmul (tensor '((1 2) (3 4))) (tensor '((5 6) (7 8))))   ; => #<tensor 2x2 [19 22 43 50]>
(transpose t) (tensor-map square t) (tensor-sum t)
(zeros '(2 3 4)) (ones '(2))

; a linear layer is three calls:
(define (linear-forward x W b) (tensor-add (matmul x W) b))

; the same expression runs through the Graph IR optimizer too (CSE/DCE) β€”
; see the Graph IR section above:
(graph-eval (lambda (x W b) (tensor-add (matmul x W) b)) X W B)
```

### Model Serialization

```lisp
; Rusty's own model format: a versioned JSON envelope over data values β€”
; numbers, strings, bools, symbols, lists, tensors. Symbols and tensors are
; tagged so they round-trip losslessly (unlike json-encode, which flattens
; symbols to strings); finite f64s are bit-exact across save/load.
(define model (list (list 'W (tensor '((0.5 1) (1.5 2))))
                    (list 'b (tensor '((4 5))))))
(save-model "model.json" model)     ; => "model.json"
(equal? model (load-model "model.json"))   ; => #t

; Graph IR state is already list data, so it serializes as-is:
(save-model "graph.json" (graph-ir (lambda (x w) (matmul x w))))

; code values are rejected by design β€” serializing live environments is
; checkpoint/restore (roadmap 3.2), not model data:
(save-model "f.json" (lambda (x) x))   ; => error
```

</details>

---

## Tail Call Optimization

Rusty implements TCO via an explicit trampoline loop β€” stack-safe recursion to arbitrary depth:

```lisp
(define (sum-to n acc)
  (if (<= n 0) acc
      (sum-to (- n 1) (+ acc n))))

(sum-to 1000000 0)   ; no stack overflow
```

**Non-tail recursion** still uses the native stack, so it is bounded β€” but it is
*guarded*, never a crash. Since 0.61.0 the evaluator sizes its recursion guard
from the thread's stack (`ulimit -s`) and raises a clean `recursion limit
exceeded` error before the stack overflows, instead of aborting the process. So
how deep non-tail recursion may go is tunable the unix way:

```bash
# deep.lisp: (define (f n) (if (= n 0) 0 (+ 1 (f (- n 1))))) (print (f 20000))
ulimit -s 8192   && rusty deep.lisp   # β†’ Error: recursion limit exceeded (native stack ~7 MB) …
ulimit -s 131072 && rusty deep.lisp   # β†’ 20000
```

The honest guidance is still to use an accumulator / tail call (unbounded via
TCO). Deeply nested *source* and printing of deeply nested values are likewise
refused/elided rather than crashing. See `benchmarks/stress_crash_probe.sh`.

---

## Testing

Golden-file suite: every check runs a `.lisp` driver and diffs its output
against a checked-in `tests/expected_*.txt` β€” **32 checks**, including one
golden per library above, an LSP protocol test, and a coverage ratchet (a
registered command with no test fails the suite).

```bash
./run_tests.sh
# or a single golden by hand (from the repo root):
cargo run --release -- tests/tests.lisp | diff - tests/expected_tests.txt
```

---

## Roadmap β€” engineering complete

Rusty was originally scoped as a five-phase plan projected across roughly five
years. In practice the engineering landed in **under a month**: from the first
prototype commit to all five phases delivered in **27 days** (2026-06-22 β†’
2026-07-19, 191 commits). What remains is adoption, not engineering.

| Phase | Deliverable | Status |
|-------|-------------|--------|
| 1 | Symbolic transformation layer β€” macro DSLs β†’ computation graphs; `defrust` compiles a numeric subset to real Rust | βœ… Delivered |
| 2 | Verifiable AI β€” self-contained verification (`check-effects` / `check-exhaustive`) + gradual typing, no external prover | βœ… Delivered |
| 3 | Native ML β€” native tensors + reverse-mode autodiff, verified bit-for-bit against PyTorch; real multicore via the multi-process seam | βœ… Delivered |
| 4 | Killer-app libraries β€” symbolic regression, synthesis, proof assistant, robotics, verified state machines, property-based testing, constraint solving, dimensional analysis (all golden-tested) | βœ… Delivered |
| 5 | Maturity β€” LSP, package manager, Python bridge, released on crates.io | βœ… Engineering complete; adoption ongoing |

[β†’ **Full phase-by-phase status**](./ROADMAP.md)

**[β†’ Read the full roadmap β†’](./ROADMAP.md)**

---

## Contributing

Rusty welcomes contributions! Areas of interest:

- **Libraries** β€” new verified, golden-tested libraries in the spirit of the
  shelf above (claim narrow, prove on declared domains)
- **Macro system** β€” new examples, DSL patterns
- **Python bridge** β€” more bindings, better interop
- **Documentation** β€” tutorials, examples, API docs
- **Tests** β€” coverage, edge cases (the suite is golden-file based; every
  change ships with a driver + expected output)

See [ROADMAP.md](./ROADMAP.md) for planned features and [ARCHITECTURE.md](./ARCHITECTURE.md) for deep-dive technical design.

---

## License

Copyright (c) 2026 Nicholas Vermeulen.

Rusty is free software licensed under the **GNU Affero General Public License,
version 3 or later** (AGPL-3.0-or-later) β€” see [LICENSE](./LICENSE). In short:
you may use, study, modify, and redistribute it, but any modified version you
run to provide a network service must make its complete source available to
that service's users.

**Commercial licensing** β€” if the AGPL's terms don't fit your use (for example,
embedding Rusty in a closed-source product or network service), a commercial
license is available on inquiry. See [COMMERCIAL.md](./COMMERCIAL.md), or contact
Nicholas Vermeulen at <thelakeman@protonmail.com> to discuss terms.

**Contributing** β€” Rusty is dual-licensed, so contributions require a short
license grant that keeps the project relicensable as a whole. See
[CONTRIBUTING.md](./CONTRIBUTING.md) for the CLA and the technical standards every
change must meet.

☯ *In memory of my brother.*