update

ober

309284f28899c2e2ecc84fd5f39e3ea9b9cdd9cb

diff --git a/AGENTS.md b/AGENTS.md
index ca30460..d9812a5 100644
--- a/AGENTS.md
+++ b/AGENTS.md
@@ -1,466 +1,52 @@
-## The Jerboa Language — Quick Reference
+# AGENTS.md for jerboa-$app Repositories
 
-Jerboa is a Scheme dialect built on Chez Scheme. It is Gerbil-inspired but its own language. **All user-facing code is `.ss` files. Never write `.sls` files for the user** — those are internal implementation files.
+These instructions apply to agents working in this repository.
 
-### File Structure
+## Language: Jerboa ONLY
 
-Every Jerboa file looks like this:
+All code in any `jerboa-*` directory or repository must be written in **Jerboa**
+— `.ss` files using `(jerboa prelude)`, `def`, `defstruct`, the Jerboa reader
+extras (`[...]` lists, `{...}` method dispatch, `keyword:` args), and
+`(std ...)` modules. The Jerboa toolchain compiles `.ss` → `.sls`.
 
-```scheme
-(import (jerboa prelude))    ;; ONE import gives you the ENTIRE language
-;; Optional extra imports for modules NOT in the prelude:
-;; (import (std net request))
+Never write raw Chez `.sls` files with `#!chezscheme` + R6RS
+`define`/`define-record-type`/`(library ...)` forms, and never write the
+application logic in another language (shell, Python, Rust, etc.) — that
+bypasses Jerboa entirely and defeats the purpose of these repos.
 
-(def (my-function x y)
-  (+ x y))
+## Build Contract
 
-(displayln (my-function 1 2))
-```
-
-Run with: `scheme --libdirs lib --script file.ss`
-
-**NEVER** write `(library ...)` forms — that's `.sls` internal syntax.
-
-### Reader Syntax Extensions
-
-```
-[...]                → plain parentheses — same as Gerbil and Chez Scheme
-{method obj args}    → (~ obj 'method args)  — method dispatch
-name:                → keyword #:name
-:std/sort            → (std sort)        — Gerbil-style module path
-#<<END ... END       → heredoc string
-```
-
-Square brackets `[...]` are interchangeable with `(...)`, exactly like Gerbil and stock Chez Scheme. You can freely use them in bindings, match clauses, and anywhere you'd use parentheses:
-```scheme
-;; All of these are correct:
-(let ([x 1] [y 2]) (+ x y))
-(for/collect ([x (in-range 5)]) (* x x))
-(match val ([list a b] (+ a b)))
-(cond [(> x 0) "positive"] [else "non-positive"])
-```
-
-### CRITICAL: Things That DO NOT EXIST in Jerboa/Chez
-
-Claude frequently hallucinates these from Gerbil, Gambit, Racket, or R7RS training data.
-**NONE of them are real in Jerboa/Chez. STOP and use the correct form.**
-
-#### AI Compatibility Aliases (these now work in the prelude)
-The following names from other Scheme dialects are aliased in `(jerboa prelude)`:
-- `hash-has-key?` → `hash-key?` (Racket)
-- `hash-table-set!` → `hash-put!` (Racket)
-- `directory-exists?` → `file-directory?` (Gambit)
-- `eql?` → `eqv?` (Common Lisp)
-- `random-integer` → `random` (Gambit)
-- `read-line` → `get-line` wrapper (Gambit) — works with or without port arg
-- `force-output` → `flush-output-port` wrapper (Gambit) — works with or without port arg
-- `string-map` → char-level map (Racket/R7RS) — `(string-map f str)`
-
-#### Hallucinated Functions (still do NOT exist)
-- `symbol<?` — use `(lambda (a b) (string<? (symbol->string a) (symbol->string b)))`
-- `string-contains?` — use `(string-contains str sub)` (returns index or #f, NOT boolean)
-- `define-struct` — use `(defstruct name (fields ...))`
-- `raise` with a string — use `(error 'who "message" irritants ...)`
-- `environment-bound?` — Gerbil-only. No direct Chez equivalent
+The Makefile must provide these targets:
 
-#### Gerbil/Gambit-isms (from training data — wrong in Jerboa)
-- `time->seconds` — use `(time-second (current-time))` for epoch seconds
-- `thread-sleep!` — Gambit. Use `(sleep (make-time 'time-duration 0 seconds))`
-- `thread-yield` — no Chez equivalent. Use `(sleep (make-time 'time-duration 0 0))` as workaround
-- `path-expand` with 2 args — Gerbil takes `(path-expand rel base)`. Jerboa takes 1 arg. Use `(path-join base rel)` for 2-arg version
-- `process-status` — Gerbil. Use `(std misc process)` API in Jerboa
-- `user-info-home` — Gerbil. Use `(getenv "HOME")`
-- `the-environment` — Gerbil. Use `(interaction-environment)` in Chez
-- `condition/report-string` — Gerbil. Use `(with-output-to-string (lambda () (display-condition c)))`
-- `make-class-type` — Gerbil. Use `(defstruct ...)` or `(defclass ...)` in Jerboa
-- `string-subst` — Gerbil. Not in prelude. Use `(string-replace str old new)` or implement manually
-- `open-fd-pair` — Gambit. Does not exist in Chez; requires different API
+- `make` prints the available targets. It must not build, install, test, or mutate artifacts.
+- `make binary` builds the application binary for the local host OS and architecture.
+- `make linux-amd64` cross-builds the Linux amd64 binary from the current host.
+- `make linux-arm64` cross-builds the Linux arm64 binary from the current host.
+- `make freebsd-amd64` cross-builds the FreeBSD amd64 binary from the current host.
 
-#### R6RS/Racket-isms (wrong variant)
-- `make-equal-hashtable` — R6RS. Use `(make-hash-table)` from Jerboa prelude
-- `arithmetic-shift` — Racket. Use `(bitwise-arithmetic-shift n k)` or `(ash n k)` in Chez
-- `pregexp-match` — Racket. Use `(std text regex)` or `(std pregexp)` API in Jerboa
+The cross-build targets must use the local cross toolchains and Jerboa/Chez
+cross-build setup. Never use Docker or Podman for building, testing, smoke
+testing, packaging, or verifying these targets.
 
-### CRITICAL: Common Arity Mistakes
-
-- `(list-of? pred)` → returns a PREDICATE. It takes 1 arg. Use: `((list-of? number?) lst)`
-- `(maybe pred)` → returns a PREDICATE. It takes 1 arg. Use: `((maybe string?) val)`
-- `(in-range end)` or `(in-range start end)` or `(in-range start end step)` — NOT `(in-range start step end)`
-- `(hash-ref ht key)` or `(hash-ref ht key default)` — NOT `(hash-ref key ht)`
-- `(string-split str delimiter)` where delimiter is a CHAR: `(string-split "a,b" #\,)`
-- `(make-rwlock)` — takes **0 args**, NOT `(make-rwlock 'name)` (Gerbil takes a name; Jerboa does not)
-- `(path-expand path)` — takes **1 arg**, NOT `(path-expand rel base)` (Gerbil takes 2; use `path-join` for 2-arg)
-- `(sort predicate list)` — Chez arg order. NOT `(sort list predicate)` which is Gerbil/SRFI order
-
-### Core Forms (all from `(import (jerboa prelude))`)
-
-#### Definitions
-```scheme
-(def x 42)                              ;; variable
-(def (f x y) (+ x y))                  ;; function
-(def (f x (y 10)) body)                ;; optional param with default
-(def (f x . rest) body)                ;; rest args
-(def* f ((x) ...) ((x y) ...))         ;; multi-arity
-(defrule (name pat) template)           ;; macro
-```
-
-#### Data Structures
-```scheme
-(defstruct point (x y))                ;; → make-point, point?, point-x, point-y, point-x-set!
-(defstruct (circle shape) (radius))    ;; inheritance (single only)
-(defmethod (area (self circle)) body)  ;; method on type
-(~ obj 'method arg ...)                ;; dispatch (or {method obj arg ...})
-(defrecord person (name age))          ;; struct + pretty-print + ->alist
-(define-enum color (red green blue))   ;; → color-red, color?, color->name
-```
-
-#### Pattern Matching
-```scheme
-(match value
-  (42 "exact")                          ;; literal
-  ((list a b c) (+ a b c))             ;; list destructure
-  ((cons h t) h)                        ;; pair
-  ((? number?) "num")                   ;; predicate
-  ((? string? s) (string-upcase s))    ;; predicate + bind
-  ((and (? number?) (? positive?)) "positive number")
-  ((or "yes" "y") #t)
-  ((=> string->number n) n)            ;; view pattern
-  (n (where (> n 0)) "positive")       ;; guard
-  (_ "default"))                        ;; wildcard
-```
-
-#### Error Handling
-```scheme
-(try expr (catch (e) handler) (finally cleanup))
-(try expr (catch (error? e) handler))
-(unwind-protect body cleanup)
-(with-resource (var init cleanup) body)
-```
-
-#### Result Type (Rust-inspired ok/err)
-```scheme
-(ok 42)  (err "bad")  (ok? r)  (err? r)
-(unwrap (ok 42))         ;; → 42 (raises on err)
-(unwrap-or (err "x") 0)  ;; → 0
-(map-ok f result)  (map-err f result)
-(and-then result f)       ;; monadic bind
-(try-result expr)         ;; exceptions → (err condition)
-(try-result* expr)        ;; exceptions → (err "message string")
-(sequence-results list-of-results)  ;; → (ok list) or first (err)
-(->? (ok 10) (+ 5) (* 2))  ;; → (ok 30), short-circuits on err
-```
+## Required Verification
 
-#### Iterators
-```scheme
-(for ((x (in-range 5))) (displayln x))
-(for/collect ((x (in-range 5))) (* x x))           ;; → (0 1 4 9 16)
-(for/fold ((sum 0)) ((x (in-range 10))) (+ sum x)) ;; → 45
-(for/or ((x lst)) (and (pred? x) x))               ;; first truthy
-(for/and ((x lst)) (pred? x))                       ;; all truthy
-
-;; Iterators: in-list, in-vector, in-string, in-range, in-hash-keys,
-;; in-hash-values, in-hash-pairs, in-naturals, in-indexed,
-;; in-port, in-lines, in-chars, in-bytes, in-producer
-```
-
-#### Threading Macros
-```scheme
-(-> x (f a) (g b))       ;; thread first: (g (f x a) b)
-(->> x (f a) (g b))      ;; thread last:  (g b (f a x))
-(as-> x v (f v) (g v))   ;; named
-(some-> x (f) (g))       ;; short-circuit on #f
-(cond-> x test (f) t2 (g))  ;; conditional steps
-(->? (ok x) (f) (g))     ;; result-aware thread first
-```
-
-#### Ergo Typing
-```scheme
-(: expr pred?)                    ;; checked cast
-(using (p (make-point 1 2) : point?)
-  (+ p.x p.y))                   ;; dot-access → (point-x p) etc.
-((list-of? number?) '(1 2 3))    ;; predicate factory → #t
-((maybe string?) #f)              ;; accepts #f or string → #t
-```
-
-#### Hash Tables
-```scheme
-(def ht (make-hash-table))
-(hash-put! ht "key" "val")
-(hash-ref ht "key")              ;; error if missing
-(hash-ref ht "key" "default")    ;; with default
-(hash-get ht "key")              ;; → val or #f
-(hash-key? ht "key")             ;; → #t/#f
-(hash-remove! ht "key")
-(hash->list ht)  (hash-keys ht)  (hash-values ht)
-(hash-for-each (lambda (k v) ...) ht)
-(list->hash-table '(("a" . 1) ("b" . 2)))
-```
-
-#### Strings
-```scheme
-(string-split "a,b,c" #\,)       ;; → ("a" "b" "c")  NOTE: char delimiter
-(string-join '("a" "b") ",")     ;; → "a,b"
-(string-trim "  hi  ")           ;; → "hi"
-(string-prefix? "he" "hello")    ;; → #t
-(string-suffix? "lo" "hello")    ;; → #t
-(string-contains "hello" "ell")  ;; → 1 (index, not boolean!)
-(string-empty? "")               ;; → #t
-(str "age: " 42 "!")             ;; → "age: 42!" (auto-coerce)
-```
-
-#### Lists
-```scheme
-(flatten '(1 (2 (3))))    ;; → (1 2 3)
-(unique '(1 2 2 3))       ;; → (1 2 3)
-(take lst n)  (drop lst n)  (take-last lst n)  (drop-last lst n)
-(every pred lst)  (any pred lst)  (filter-map f lst)
-(group-by f lst)  (zip lst1 lst2)  (frequencies lst)
-(partition pred lst)  (interleave l1 l2)  (mapcat f lst)
-(distinct lst)  (keep f lst)  (split-at lst n)
-(append-map f lst)  (snoc lst elem)
-```
-
-#### Functional Combinators
-```scheme
-(compose f g)  (comp f g)       ;; (f (g x))
-(partial f arg ...)              ;; partial application
-(complement pred)  (negate pred) ;; logical not
-(identity x)  (constantly v)     ;; basic combinators
-(curry f arg)  (flip f)          ;; currying, arg swap
-(conjoin p1 p2)  (disjoin p1 p2) ;; predicate AND/OR
-(juxt f g)                        ;; → (lambda (x) (list (f x) (g x)))
-(cut f <> y)                      ;; SRFI-26 partial: (lambda (x) (f x y))
-```
-
-#### JSON
-```scheme
-(string->json-object "{\"key\":\"val\"}")  ;; → hash table
-(json-object->string ht)                    ;; → JSON string
-(read-json port)  (write-json obj port)
-```
-
-#### CSV
-```scheme
-(csv->alists "name,age\nAlice,30")  ;; → (((name . "Alice") (age . "30")))
-(read-csv-file "data.csv")          ;; → list of row lists
-(write-csv-file "out.csv" rows)
-```
-
-#### DateTime
-```scheme
-(datetime-now)  (datetime-utc-now)
-(make-datetime 2026 3 27 12 0 0)
-(parse-datetime "2026-03-27T12:00:00Z")
-(datetime->iso8601 dt)  (datetime->epoch dt)
-(datetime-add dt duration)  (datetime-diff dt1 dt2)
-(datetime<? dt1 dt2)  (day-of-week dt)  (leap-year? 2024)
-```
-
-#### Paths
-```scheme
-(path-join "/home" "user" "f.txt")  ;; → "/home/user/f.txt"
-(path-directory "/a/b/f.txt")       ;; → "/a/b"
-(path-extension "file.txt")         ;; → "txt"
-(path-absolute? "/home")            ;; → #t
-```
-
-#### File I/O
-```scheme
-(read-file-string "f.txt")         ;; → entire file
-(read-file-lines "f.txt")          ;; → list of lines
-(write-file-string "f.txt" "data")
-```
+Before committing or pushing any code, all required build targets must succeed:
 
-#### Pretty Printing
-```scheme
-(pp expr)  (pp-to-string expr)  (pprint expr)
+```sh
+make
+make binary
+make linux-amd64
+make linux-arm64
+make freebsd-amd64
 ```
 
-#### Formatting
-```scheme
-(format "~a is ~a" "Alice" 30)  ;; → "Alice is 30"
-(printf "x = ~a\n" 42)
-(displayln "hello" " " "world")
-```
-
-#### Anaphoric / Conditional Binding
-```scheme
-(awhen (find x) (use it))          ;; binds result to `it`
-(aif (find x) (use it) (default))
-(when-let (x (find y)) (use x))
-(if-let (x (find y)) (use x) (default))
-```
-
-#### Loops
-```scheme
-(while test body ...)
-(until test body ...)
-(dotimes (i 10) body ...)   ;; 0..9
-```
-
-#### Misc Sugar
-```scheme
-(assert! (> x 0))  (assert! (> x 0) "message")
-(alist (name "Alice") (age 30))  ;; → ((name . "Alice") (age . 30))
-(let-alist data (name age) body)
-```
-
-### What's NOT in the Prelude (requires separate import)
-
-```scheme
-(import (std net request))      ;; HTTP client
-(import (std net httpd))        ;; HTTP server
-(import (std db sqlite))        ;; SQLite
-(import (std actor))            ;; Actor system
-(import (std async))            ;; Async/await
-(import (std crypto digest))    ;; SHA, MD5
-(import (std text regex))       ;; Regex
-(import (std text xml))         ;; XML
-(import (std text yaml))        ;; YAML
-(import (std os env))           ;; Environment variables
-(import (std os signal))        ;; Signal handling
-(import (std security sandbox)) ;; Sandboxing
-```
-
-### Chez Scheme Conflicts (handled by prelude)
-
-The prelude shadows these Chez builtins with Jerboa versions:
-`make-hash-table`, `hash-table?`, `sort`, `sort!`, `printf`, `fprintf`,
-`path-extension`, `path-absolute?`, `with-input-from-string`,
-`with-output-to-string`, `iota`, `1+`, `1-`, `partition`,
-`make-date`, `make-time`
-
-All standard Chez Scheme is still available — the prelude just re-exports
-improved versions of the above.
-
----
-
-## Repository Boundaries
-
-When working in a Jerboa project, **ONLY modify files in the current repo** unless the user explicitly names another path.
-
-Common sibling repos that exist but must NOT be touched without explicit instruction:
-- `~/mine/jerboa-emacs` — **NEVER touch**. Another model owns it.
-- `~/mine/jerboa-mcp` — Only modify when user explicitly says to work there.
-- `~/mine/jerboa-shell` — Only modify when user explicitly says to work there.
-- `~/mine/gerbil-mcp` — **NEVER touch**. Deprecated.
-- `~/mine/gerbil-orig` — Read-only reference for upstream Gerbil. Never modify.
-
-If a user instruction mentions a file path, use EXACTLY that path. Do not substitute a similar-looking path from another repo.
-
----
-
-## Build & Verification
-
-After modifying any `.ss` or `.sls` (Jerboa Scheme) source files, always run the build command and fix any errors before moving on. Common issues include: missing imports, wrong function names, and duplicate definitions.
-
-### Stale Artifacts
-
-If edits seem to have no effect after `make build`, delete stale compiled files:
-```bash
-find lib -name "*.so" -delete && find lib -name "*.wpo" -delete && make build
-```
-Run `jerboa_stale_static` to detect stale `.so` files before debugging "why doesn't my edit work?".
-
-## Jerboa MCP Tools — MANDATORY Usage
-
-Jerboa is a niche Scheme dialect with limited training data. **Never guess — always verify** with MCP tools. Tool descriptions are available at runtime via the MCP server; this section covers **when** and **why** to use each tool.
-
-### MANDATORY Workflow Order (for writing new code)
-
-1. **`jerboa_howto`** — BEFORE writing code, search cookbook for verified patterns
-2. **`jerboa_module_exports`** / **`jerboa_function_signature`** — confirm APIs exist and check arities
-3. Write the code
-4. **`jerboa_verify`** — combined syntax + compile + lint + arity + duplicate check (use instead of individual tools)
-5. **`jerboa_security_scan`** — for code involving FFI, shell commands, or file I/O
-
-### Essential Tools (use proactively)
-
-| When | Tool |
-|---|---|
-| Before writing ANY Jerboa code | `jerboa_howto` — cookbook has verified patterns with correct imports |
-| Check what a module exports | `jerboa_module_exports` — never guess function names |
-| Check function arity/args | `jerboa_function_signature` — prevents wrong-arg-count errors |
-| Validate your code | `jerboa_verify` — one-stop syntax+compile+lint+arity check |
-| Test expressions interactively | `jerboa_eval` — use `imports` param for modules, `env` for FFI paths |
-| Persistent interactive testing | `jerboa_repl_session` — maintains state across evaluations |
-| Debug an error message | `jerboa_explain_error` + `jerboa_error_fix_lookup` |
-| Understand unfamiliar code | `jerboa_file_summary` + `jerboa_document_symbols` |
-| Find where something is defined | `jerboa_find_definition` — source file, module, kind, arity |
-| Search for symbol by substring | `jerboa_apropos` or `jerboa_smart_complete` |
-| Build the project | `jerboa_build_and_report` or `jerboa_make` — prefer over bash `make` |
-| Run tests | `jerboa_run_tests` — prefer over bash `scheme --script` |
-| Check for stale .so artifacts | `jerboa_stale_static` — common cause of "edit has no effect" |
-| Macro expansion | `jerboa_expand_macro` / `jerboa_trace_macro` |
-| Inspect struct/class types | `jerboa_class_info` — fields, inheritance, constructor signature |
-| FFI work | `jerboa_ffi_scaffold` / `jerboa_ffi_type_check` / `jerboa_ffi_null_safety` |
-| Port Gerbil code | `jerboa_migration_check` + `jerboa_translate_scheme` |
-| Detect paren imbalance | `jerboa_check_balance` — use BEFORE `make build` after deep edits |
-| Full project audit | `jerboa_project_health_check` — balance, exports, cycles, duplicates |
-| Security audit | `jerboa_security_audit` + `jerboa_import_policy_check` |
-| Static build audit | `jerboa_static_symbol_audit` + `jerboa_boot_library_audit` |
-| Explore a module | `jerboa_module_catalog` (replaces multiple `jerboa_doc` calls) |
-| Look up any symbol | `jerboa_doc` — type, arity, qualified name, related symbols |
-| Read stdlib source | `jerboa_stdlib_source` — see internal implementations |
-
-### Cookbook & Knowledge Management
-
-- **`jerboa_howto`** / **`jerboa_howto_get`**: Search and retrieve verified recipes
-- **`jerboa_howto_add`**: Save new patterns to cookbook (MANDATORY when you discover something non-trivial)
-- **`jerboa_howto_run`** / **`jerboa_howto_verify`**: Validate recipes still work
-- **`jerboa_error_fix_add`**: Save error→fix mappings for common mistakes
-
-### Code Generation & Refactoring
-
-`jerboa_rename_symbol`, `jerboa_balanced_replace`, `jerboa_wrap_form`, `jerboa_splice_form`, `jerboa_scaffold_test`, `jerboa_generate_module`, `jerboa_translate_scheme`, `jerboa_project_template`, `jerboa_httpd_handler_scaffold`, `jerboa_db_pattern_scaffold`, `jerboa_actor_ensemble_scaffold`
-
-### Feature Suggestions
-
-- **`jerboa_list_features`** / **`jerboa_suggest_feature`** / **`jerboa_vote_feature`**: Track and submit tooling improvement ideas
-
----
-
-## MANDATORY: Save What You Learn
-
-Jerboa is niche — every non-trivial pattern you discover prevents future sessions from re-discovering it.
-
-### Save to cookbook (`jerboa_howto_add`) whenever you:
-- Discover a working pattern through `jerboa_eval` or trial-and-error
-- Figure out correct imports, arities, or calling conventions that weren't obvious
-- Find a workaround for a Jerboa quirk or undocumented behavior
-
-**Before saving**: check `jerboa_howto` to avoid duplicates. **Do NOT save**: trivial one-liners, project-specific logic, or existing recipes.
-
-**Recipe format**: `id` (kebab-case), `tags` (4-6 search keywords incl. module name), `imports` (all required), `code` (complete working example), `notes` (gotchas/alternatives).
-
-### Suggest tooling improvements (`jerboa_suggest_feature`) whenever you:
-- Make multiple sequential tool calls that could be one tool
-- Fall back to bash because an MCP tool is missing or insufficient
-
-**Before suggesting**: check `jerboa_list_features`; vote with `jerboa_vote_feature` if it already exists.
-
-### Save Discoveries Mechanisms
-- **`/save-discoveries` skill**: invoke anytime to review session and save patterns + suggestions
-- **PreCompact hook**: add `PreCompact` hook with `type: "prompt"` in `.claude/settings.json` to auto-save before context compaction
-
----
-
-## Common Workflows
-
-- **Write new code**: `jerboa_howto` -> `jerboa_module_exports` -> write code -> `jerboa_verify` -> `jerboa_security_scan`
-- **Debug an error**: `jerboa_explain_error` -> follow suggested tools -> `jerboa_howto` for fix patterns
-- **Understand unfamiliar code**: `jerboa_file_summary` -> `jerboa_document_symbols` -> `jerboa_module_deps`
-- **Refactor a module**: `jerboa_check_exports` -> `jerboa_find_callers` -> `jerboa_rename_symbol` -> `jerboa_check_import_conflicts`
-- **Build project**: `jerboa_build_conflict_check` -> `jerboa_make` -> `jerboa_build_and_report`
-- **Port from Gerbil**: `jerboa_migration_check` -> `jerboa_translate_scheme` -> `jerboa_verify` -> `jerboa_check_syntax`
-- **Audit project quality**: `jerboa_verify` -> `jerboa_lint` -> `jerboa_dead_code` -> `jerboa_dependency_cycles`
-- **Debug a crash**: `jerboa_stale_static` -> `jerboa_bisect_crash` -> `jerboa_ffi_type_check`
-- **Learn a module**: `jerboa_stdlib_source` -> `jerboa_module_catalog` -> `jerboa_module_quickstart`
-- **Security audit**: `jerboa_security_audit` -> `jerboa_import_policy_check` -> `jerboa_unsafe_import_lint`
-- **Static build audit**: `jerboa_static_symbol_audit` -> `jerboa_boot_library_audit` -> `jerboa_rust_musl_build`
-- **Safe-by-default check**: `jerboa_safe_prelude_check` -> `jerboa_resource_leak_check` -> `jerboa_safe_prelude_generate`
-- **Debug editor command**: `jerboa_command_trace` with `project_path` and `buffer_type`
+Do not commit or push if any target fails. If a cross-build cannot run because
+the host is missing a required toolchain or sysroot, stop and report the missing
+dependency instead of committing or pushing partially verified code.
 
-## Workflow Conventions
+## Working Rules
 
-When implementing new features, always complete the documentation update in the same session. Document non-trivial solutions as howto recipes in the cookbook system.
+- Keep changes inside the current repository unless the user explicitly names another path.
+- Preserve the target semantics above when editing the Makefile.
+- Treat the cross-build targets as release-critical, not optional follow-up work.
+- Do not add Dockerfiles, container scripts, Podman invocations, or container-based verification paths.
diff --git a/lib/jerboa-awk/runtime.sls b/lib/jerboa-awk/runtime.sls
index 2a7d59f..c10d511 100644
--- a/lib/jerboa-awk/runtime.sls
+++ b/lib/jerboa-awk/runtime.sls
@@ -47,7 +47,9 @@
     make-equal-hash-table hash-ref hash-put! hash-key? hash-remove!
     hash-clear! hash-length hash-for-each
     ;; String helpers
-    string-join string-contains string-prefix?)
+    string-join string-contains string-prefix?
+    ;; Regex cache
+    make-regex-cache regex-cache-lookup)
 
   (import (chezscheme)
           (only (jerboa runtime)
@@ -55,7 +57,8 @@
             hash-clear! hash-length hash-for-each)
           (only (std misc string)
             string-join string-contains string-prefix? string-trim)
-          (jerboa-awk value) (jerboa-awk ast) (std pregexp))
+          (jerboa-awk value) (jerboa-awk ast) (std pregexp)
+          (std os env))
 
   ;;; ---- Local helpers ----
 
@@ -66,6 +69,21 @@
   ;; Alias for backward compat — string-trim trims both ends
   (define string-trim-both string-trim)
 
+  ;;; ---- Regex pattern cache ----
+
+  (define *regex-cache* (make-regex-cache))
+
+  (define (make-regex-cache)
+    (make-hashtable equal-hash equal?))
+
+  (define (regex-cache-lookup cache pattern)
+    (let ((cached (hashtable-ref cache pattern #f)))
+      (if cached
+        (values cached #t)
+        (let ((compiled (pregexp pattern)))
+          (hashtable-set! cache pattern compiled)
+          (values compiled #f)))))
+
   ;;; ---- Execution state ----
 
   (define-record-type awk-env
@@ -332,17 +350,20 @@
             (begin (close-port from) (close-port err) (reverse lines))
             (loop (cons line lines)))))))
 
-  (define (env-init-environ! env)
-    (let ((arr (env-get-array env 'ENVIRON)))
-      (let ((envlist (get-all-environ)))
-        (for-each
-         (lambda (entry)
-           (let ((eq-pos (string-contains entry "=")))
-             (when eq-pos
-               (hash-put! arr
-                          (substring entry 0 eq-pos)
-                          (make-awk-string (substring entry (+ eq-pos 1) (string-length entry)))))))
-         envlist))))
+  (define env-init-environ!
+    (let ((initialized? #f))
+      (lambda (env)
+        (unless initialized?
+          (set! initialized? #t)
+          (let ((arr (env-get-array env 'ENVIRON)))
+            (for-each
+             (lambda (entry)
+               (let ((eq-pos (string-contains entry "=")))
+                 (when eq-pos
+                   (hash-put! arr
+                              (substring entry 0 eq-pos)
+                              (make-awk-string (substring entry (+ eq-pos 1) (string-length entry)))))))
+             (get-all-environ)))))))
 
   ;;; ---- I/O port management ----