Rust ScriptBots
Deterministic artificial-life simulator in Rust: agent-based evolution with pluggable brain implementations, GPU-accelerated UI, DuckDB analytics, and LLM-in-the-loop experimentation
README
Rust ScriptBots
ScriptBots is a modern Rust reimagining of Andrej Karpathy’s classic agent-based evolution simulator. Our goal is a faithful, deterministic port with a GPU-accelerated UI, pluggable brain implementations, and first-class analytics. This is a multi-crate Cargo workspace separating simulation core, renderer-neutral runtime protocol, brains, storage, rendering, and the application shell.
The authoritative roadmap is PLAN_TO_REARCHITECT_AND_REVIVE_RUST_SCRIPTBOTS.md. The older GPUI port plan is retained as historical design evidence, and a sibling WebAssembly plan lives in PLAN_TO_CREATE_SIBLING_APP_CRATE_TARGETING_WASM.md.
Philosophy & purpose
- Why this exists: ScriptBots is a minimalist artificial life laboratory. By rebuilding the original simulator with rigorously deterministic Rust systems, we can observe, measure, and reproduce emergent behavior at scale—without undefined behavior or global state muddying results.
- What we learn: How simple sensory channels and local rules produce complex population dynamics—cooperation vs. predation, resource gradients shaping migration, lineage divergence under different mutation schedules, and the role of perception in survival.
- LLM-in-the-loop science: The REST API, CLI, and MCP HTTP server expose the full control surface (knobs, patches, snapshots). This lets an external LLM agent act as an autonomous lab assistant: steering experiments, sweeping parameter spaces, logging observations into FrankenSQLite, and drafting human-readable reports.
- Example workflows:
- Parameter sweeps: vary
mutation.{primary,secondary}and temperature gradients; record birth/death ratios and equilibrium populations. - Interventions: toggle
closedworlds, inject carnivore cohorts, or freeze food diffusion to test resilience. - Reporting: query or export FrankenSQLite tables to generate charts and tables describing discovered phenomena (e.g., altruistic giving thresholds that stabilize mixed diets).
- Parameter sweeps: vary
- Example workflows:
- A brain testbed: The
Braintrait and registry allow swapping decision engines—handwritten controllers, MLP/DWRAON/Assembly, or NeuroFlow—while holding the environment constant. This enables fair comparisons of:- Perception encoding (multi-eye vision, smell/sound/blood) and how architectures exploit them.
- Locomotion control (exact legacy two-rotation parity by default, selectable differential drive) and energy/health trade-offs.
- Evolutionary operators (mutation/crossover) and speciation pressures.
- Reproducible research: Deterministic pipelines + a growing replay roadmap mean results can be shared and re-run bit-for-bit, making the project a solid platform for pedagogy, papers, and benchmarking new brain designs.
Why this exists
- Determinism and safety: Replace legacy C++/GLUT and global state with idiomatic Rust, tightly audited platform-boundary
unsafe, and reproducible runs. - Performance at scale: Data-parallelism (Rayon) and cache-friendly layouts to simulate thousands of agents efficiently.
- Modern UX: Declarative, GPU-accelerated GPUI interface with an inspector, overlays, and smooth camera controls.
- Observability: Persist metrics and snapshots to FrankenSQLite for replay, analytics, and regression testing while the UI consumes lock-free immutable read models.
- Extensibility: Hot-swap brain implementations (MLP, DWRAON, experimental Assembly, plus optional NeuroFlow) without rewriting the world loop.
Architecture at a glance
The workspace is organized for clear boundaries and fast incremental builds:
rust_scriptbots/
├── Cargo.toml # Workspace manifest, shared deps/lints/profiles
├── rust-toolchain.toml # Pinned nightly-2026-07-09 toolchain (MSRV 1.89)
├── crates/
│ ├── scriptbots-core # Simulation core (WorldState, AgentState, tick pipeline, config)
│ ├── scriptbots-runtime # Sole-owner HostCore, protocol, fixed-deadline lifecycle, null frontend
│ ├── scriptbots-brain # Brain trait + base implementations (mlp, dwraon, assembly)
│ ├── scriptbots-brain-ml # Candle/Tract/tch probes plus the optional Frankentorch FtBrain
│ ├── scriptbots-brain-neuro# NeuroFlow brain (optional), feature-gated
│ ├── scriptbots-index # Uniform-grid index; alternate backends are not implemented
│ ├── scriptbots-storage # FrankenSQLite persistence worker & analytics snapshots
│ ├── scriptbots-render # GPUI integration and visual layer (HUD, canvas renderer)
│ ├── scriptbots-app # Binary crate wiring everything together
│ └── scriptbots-web # Sibling WebAssembly harness (wasm-bindgen bindings; experimental)
└── docs/
└── wasm/ # ADRs, browser matrix, multithreading notes, rendering spikes
└── original_scriptbots_code_for_reference/ # Upstream C++ snapshot for parity
Architecture diagram (high-level)
Data flows left-to-right; control surfaces are orthogonal and non-invasive:
┌───────────────────────────────────────┐
│ scriptbots-brain family │
│ (brain, brain-ml, brain-neuro) │
└──────────────┬────────────────────────┘
│ BrainRegistry (attach by key)
┌──────────────────────────────────────────▼──────────────────────────────────────────┐
│ scriptbots-core (WorldState, Tick Pipeline) │
│ - SoA AgentColumns · Spatial index (scriptbots-index) │
│ - Deterministic: sense → brains → actuation → persistence projection │
└───────────────┬───────────────────────────┬───────────────────────────┬─────────────┘
│ AgentSnapshots │ StepOutcome │ ControlCommand ↑ / disposition ↓
│ │ + Arc<PersistenceBatch> │
┌───────▼────────┐ ┌────────▼───────────────┐ ┌─────▼──────────┐
│ Renderer (GUI) │ │ Application runtime │ │ Application │
│ GPUI window │ │ PersistenceAdmission │ │ command driver│
│ or Terminal TUI│ │ Session / step driver │ └─────┬───────────┘
│ (console text) │ └────────┬───────────────┘ │
└───────┬────────┘ │ admitted batch │
│ World snapshots ┌────────▼───────────┐ │
│ HUD metrics │ scriptbots-storage │ │
┌───────▼────────┐ │ StoragePipeline │ │
│ scriptbots- │ └────────┬───────────┘ │
│ render │ ▼ │
└────────────────┘ ┌──────────────────────┐ │
│ FrankenSQLite │ │
│ run-name.sqlite │ │
└──────────────────────┘ │
│
┌─────────────────────────────────────────▼────────────────────┐
│ scriptbots-app (orchestrator) │
│ - launches ControlRuntime (Tokio thread) │
│ - owns CommandBus and drains one ordered vector per boundary │
│ - selects Renderer (CLI flag/env) │
│ - seeds world, installs brains, primes history │
└───────────────┬───────────────────────────────┬──────────────┘
│ REST (axum + Swagger UI) │ MCP HTTP (mcp_protocol_sdk)
│ /api/knobs /api/config │ tools: list_knobs,get_config,
│ /api/knobs/apply PATCH config │ apply_updates,apply_patch
│ │
│ │
┌──────▼───────┐ │
│ control_cli │ (reqwest; TUI watch) │
│ list/get/set │ -> REST │
└──────────────┘ │
│
┌────────────────────────────────────────────────────────────────────────────────▼─────────────┐
│ scriptbots-web (wasm) │
│ - wasm-bindgen: default_init_options/init_sim/tick/snapshot/reset/registerBrain │
│ - snapshot_format: json | binary (Postcard) · wasm-vs-native parity tests │
│ - feeds JS renderer (WebGPU/Canvas) │
└──────────────────────────────────────────────────────────────────────────────────────────────┘
- Background workers:
StoragePipelineis a bounded-admission writer whose dedicated thread creates and exclusively owns its FrankenSQLite connection;ControlRuntime(Tokio) is separately isolated. A successful enqueue is not a durability receipt: flush or shutdown must acknowledge the earlier transactions. Application drivers drain one ordered command vector at a boundary; core applies world-owned mutations and returns normalized playback explicitly instead of owning a second queue. - Startup is fail-closed and transactional. Renderer selection and control-environment validation happen first, then every enabled REST/MCP socket is prebound and held before configuration output, auto-tuning, process-priority changes, world construction, or storage reservation. Launch consumes those exact listeners, so a bind failure cannot leave config, tuning, or run-database artifacts behind and cannot race a later rebind.
- REST and MCP run as supervised sibling tasks. An unexpected error or clean task exit stops the sibling, preserves the original failure as the root cause, and publishes failed runtime health; the TUI, GPUI, and Bevy frontends observe that health and terminate with the same root failure. Graceful shutdown joins both tasks and releases both listeners.
- That supervision guarantee covers ordinary returned errors and task exits. Debug/test builds use unwinding boundaries to exercise panic reporting, while the shipped
panic = "abort"release profile intentionally cannot recover from a panic or promise destructor-based cleanup after one. - Frontends do not query FrankenSQLite or wait on a storage mutex during paint. The presentation boundary is not yet complete, however: GPUI contains its characterized double-drive by making the HUD the sole interim simulation driver and the world window read-only, and it now services explicit playback before paused/accumulator early returns.
HostCoreand its native lifecycle are implemented, but TUI/GPUI/Bevy and the live server transports remain on their interim adapters until the dedicated migration beads replace them. scriptbots-runtimeowns the renderer-neutral command, two-axis status, snapshot, event-cursor, and manual-drive contracts plus the exact sole-ownerHostCore. Command lifecycle schema v1 retains the exact envelope, client namespace/sequence, optional admission order, typed control/scientific/config guards, and contiguous application transitions; every terminal runtime outcome has a journal obligation. Its optionalnative-asupersyncfeature drives that same!Sendhost as one current-thread root future at absolute deadlines. Commands and journal-ready signals wake the owner, catch-up is bounded and reported, quiescent paused worlds have no periodic timer, and ordered shutdown retains its exact host and journal obligations without spawning or detaching a simulation task. The lifecycle persistence source is committed underbd-2z0.5.2; centralized DSR proof is pending.- Brain introspection is an explicit read-only projection, never a per-tick side effect. A client issues a revisioned request for up to eight stable
AgentUidvalues; core and each brain family enforce independent bounds for layers, names, values, edges, source scalars, and retained payload. Responses carry the exact source tick and typed unavailable/clipped status, while TUI and GPUI cache by client, stable identity, and source tick. With no request, no brain is inspected and NeuroFlow performs no inspection JSON serialization; digest-neutrality and next-output purity are tested across the supported families. - Control surfaces are transport-agnostic; both REST and MCP use the same safe
ControlHandleand enqueue commands with back-pressure.
Crate roles
scriptbots-core: Simulation core withWorldState,AgentState, deterministic staged tick pipeline, config, sensor/actuation scaffolding, and brain registry bindings.scriptbots-runtime: Renderer-neutral command, immutable lifecycle evidence, two-axis status, snapshot, and event-cursor contracts with typed revision domains; the sole-ownerHostCore; a pure injected-time fixed-deadline adapter; and an optional Asupersync=0.3.9native lifecycle. It depends on core but not storage, servers, or renderers. The default and WASM-facing surface remains runtime-neutral; Asupersync is activated only bynative-asupersyncon native targets.scriptbots-brain:Braintrait + baseline implementations and adapters; experimentalassemblybehind a feature.scriptbots-brain-ml: Feature selection and the retainedml.placeholdersensor-copy probe for Candle, Tract, and tch, plus the non-default code-first FrankentorchFtBrainfamily. The FtBrain source is implemented underbrain-ft; its pinned DSR compile, determinism, and benchmark proof remains pending underbd-2z0.3.12.3.scriptbots-brain-neuro: Optional NeuroFlow-based brain; controllable at runtime via config/env (see below).scriptbots-index: Production uniform-grid neighborhood index. The declaredrstarandkddependency features are compile-time scaffolding, not implemented index backends.scriptbots-storage: FrankenSQLite persistence with transactional batched writes, bounded admission, explicit flush/shutdown commit receipts, and immutable latest-value analytics snapshots for frontends.scriptbots-render: GPUI UI layer with a window shell, HUD, canvas renderer for agents/food, selection highlights, and diagnostics overlay.scriptbots-app: Binary shell. Wires tracing/logging, config/env, storage pipeline, installs brains, seeds agents, and launches the GPUI shell.scriptbots-web: WebAssembly harness exposing bindings to init/tick/reset and snapshot the simulation; consumesscriptbots-corewithdefault-features = false(sequential fallback; Rayon disabled on wasm).
Current status
- Workspace scaffolding, shared lints, and profiles are in place.
scriptbots-core: World state, agent runtime, staged tick, reproduction/combat hooks, history summaries, and brain registry integration are implemented; parity tasks are tracked in the plan doc.scriptbots-runtime: the protocol boundary, typed command/revision/status domains, schema-v1 lifecycle evidence, opaque client ports, cursors, manual-drive contract, sole-ownerHostCore, pure fixed-deadline driver, and optional current-thread Asupersync lifecycle are implemented. Thebd-2z0.5.2lifecycle source is centralized-DSR-pending; legacy frontend and transport migration remains pending.scriptbots-render: GPUI window + HUD + canvas renderer with camera controls, selection highlights, and diagnostics overlay; audio is optional viakirafeature.scriptbots-app: explicit renderer selection, pre-storage control-socket reservation, supervised REST/MCP lifecycle, and frontend health propagation are implemented. The full cross-feature/platform startup matrix remains a Phase 0.4 acceptance gate.scriptbots-brain: MLP and DWRAON implementations are enabled by default; Assembly remains experimental; registry wiring is present.scriptbots-brain-neuro: NeuroFlow-backed brain available behind theneurofeature (runtime toggles below).scriptbots-storage: the exact FrankenSQLite source is pinned; its bounded worker now has a file-backed durable outbox, stable per-batch identities, monotonic admitted/applied/durable watermarks, ordered startup recovery, controller wait deadlines, supervised worker ownership, exact recovery identity/schema proof, and a V6 run-scoped base schema with V7 canonical host archives. Code-first V8 domain-event and V9 command-lifecycle projections are committed underbd-2z0.5.2, with centralized DSR proof pending. Same-thread writes use the same outbox protocol, and terminal receipt/join errors preserve one exact typed root cause. Deadlines do not cancel an in-flight database call or bound the supervised reaper. Strict-run host policy, pairwise interactions, checkpoint/replay integration, and run bundles remain open.
See the active recovery roadmap in PLAN_TO_REARCHITECT_AND_REVIVE_RUST_SCRIPTBOTS.md for staged milestones and acceptance gates. The older GPUI port plan is historical evidence, not current policy.
Getting started
Prerequisites
- Rust toolchain:
nightly-2026-07-09, pinned inrust-toolchain.toml; the
workspace and locked dependency graph declare a minimum Rust version of 1.89.
Install throughrustup. - OS: Linux, macOS, or Windows 11 (native or WSL2). GPU drivers should be up to date for best GPUI performance (wgpu backends: Metal/macOS, Vulkan/Linux, D3D12 or Vulkan/Windows).
Build
cargo check
CPU tuning note: Workspace builds now default to a portable baseline so CI runners don’t require AVX2/“native” features. Set
RUSTFLAGS="-C target-cpu=native"locally (all launch scripts already do this) if you want host-specific tuning.
Run the app shell
cargo run -p scriptbots-app
Recommended defaults for performance
- Threads: By default, the core auto-budgets worker threads conservatively. Our profiling shows best throughput at 8 threads on a 32-core CPU for this workload. To match that:
SCRIPTBOTS_MAX_THREADS=8 cargo run -p scriptbots-app -- --storage memory --storage-thresholds 128,4096,1024,1024
- With servers disabled (avoid port conflicts/background overhead):
SCRIPTBOTS_CONTROL_REST_ENABLED=false \
SCRIPTBOTS_CONTROL_MCP=disabled \
SCRIPTBOTS_MAX_THREADS=8 \
cargo run -p scriptbots-app -- --mode terminal --storage memory --storage-thresholds 128,4096,1024,1024
- Profiling helpers (headless):
# No storage (isolates world.step performance)
SCRIPTBOTS_MAX_THREADS=8 cargo run -p scriptbots-app -- --profile-steps 1000
# With storage (memory) and tuned flush thresholds
SCRIPTBOTS_MAX_THREADS=8 cargo run -p scriptbots-app -- --profile-storage-steps 3000 --storage memory --storage-thresholds 128,4096,1024,1024
Set logging verbosity with RUST_LOG, for example:
RUST_LOG=info cargo run -p scriptbots-app
Terminal-only mode
-
Force the emoji TUI renderer (useful on headless machines):
SCRIPTBOTS_MODE=terminal cargo run -p scriptbots-app -
Auto selection:
SCRIPTBOTS_MODE=auto(default) chooses only a renderer compiled into the binary—GPUI first, then Bevy—and only in a real native graphical session; otherwise it uses the terminal. Linux/Unix sessions require a display environment; local macOS sessions use the native window system without X11 variables, while SSH sessions choose the terminal. -
Auto-mode policy overrides:
SCRIPTBOTS_FORCE_TERMINAL=1→ choose terminal in Auto mode even when a display server is present.SCRIPTBOTS_FORCE_GUI=1→ require compiled GPUI in Auto mode even if no display variables are set; unavailable features and launch failures are errors, never terminal fallbacks.
-
CI/headless smoke runs can bypass raw TTY requirements by setting
SCRIPTBOTS_TERMINAL_HEADLESS=1, which drives the renderer against an in-memory buffer for a few frames. -
Emoji mode (terminal renderer):
- Defaults ON when a modern UTF‑8 terminal is detected; press
eto toggle at runtime. - Force enable via env:
SCRIPTBOTS_TERMINAL_EMOJI=1|true|yes|on; force disable with0|false|off|no. - Heuristic: enabled if
TERMis notdumb/linux/vt100, locale containsutf-8|utf8, andCIis unset. - Emoji mappings: terrain
🌊/💧/🏜/🌿/🌺/🪨(lush swaps:🐟,🌴,🌾, barren🥀); agents single🐇/🦝/🦊, small groups🐑/🐻/🐺, large cluster👥, boosted🚀, spike peak⚔(underline). Heading arrows remain for single agents when available. - If emojis render as tofu/misaligned, install an emoji-capable font (e.g., Noto Color Emoji) or toggle off with
e.
- Defaults ON when a modern UTF‑8 terminal is detected; press
-
Narrow symbols mode: press
nto switch to width-1 friendly symbols while keeping emoji colors off-background; helpful for strict terminals/alignment. -
Headless report (CI-friendly):
SCRIPTBOTS_MODE=terminal \ SCRIPTBOTS_TERMINAL_HEADLESS=1 \ SCRIPTBOTS_TERMINAL_HEADLESS_FRAMES=24 \ SCRIPTBOTS_TERMINAL_HEADLESS_REPORT=terminal_report.json \ cargo run -p scriptbots-app -- --storage memory --threads 2This renders offscreen for N frames and writes a JSON summary (frames, ticks, births/deaths, energy stats) to
terminal_report.json.
Quick start (platform scripts)
Use the convenience scripts in the repo root to launch ScriptBots with sensible defaults per OS. These scripts set appropriate targets, isolate build artifacts, and pick the right renderer.
Linux — terminal mode
- Script:
run_linux_terminal_mode.sh - Usage:
chmod +x ./run_linux_terminal_mode.sh ./run_linux_terminal_mode.sh - What it does:
- Detects CPU count into
THREADS(nproc/getconffallback; override by exportingTHREADSbeforehand) - Builds with native CPU optimizations (
RUSTFLAGS="-C target-cpu=native") - Forces terminal renderer (
SCRIPTBOTS_MODE=terminal) - Runs release binary with cargo job parallelism
-j $THREADSand passes--threads $THREADSto the app
- Detects CPU count into
- Customize:
- Reduce CPU usage:
THREADS=2 ./run_linux_terminal_mode.sh - Headless CI snapshot: export
SCRIPTBOTS_TERMINAL_HEADLESS=1to render against an in-memory buffer - Logging:
RUST_LOG=info ./run_linux_terminal_mode.sh
- Reduce CPU usage:
Linux — Bevy renderer (Vulkan/GL)
- Script:
run_linux_with_bevy.sh - Usage:
chmod +x ./run_linux_with_bevy.sh ./run_linux_with_bevy.sh - What it does:
- Detects CPU count (caps default at 8) and passes the value to cargo (
-j) and the app (--threads) - Prefers Vulkan via
WGPU_BACKEND=vulkan, falling back to GL when Vulkan is unavailable - Enables the Bevy renderer (
--features bevy_render) and launches with--mode bevy - Sets high-performance WGPU hints (
SB_WGPU_PRESENT_MODE=full, bloom/tonemap/fog defaults) matching the Windows helper
- Detects CPU count (caps default at 8) and passes the value to cargo (
- Customize:
- Limit load:
THREADS=4 ./run_linux_with_bevy.sh - Force GL:
WGPU_BACKEND=gl ./run_linux_with_bevy.sh - Append extra flags after the final
--(e.g.,./run_linux_with_bevy.sh -- --debug-watermark)
- Limit load:
macOS — terminal console
- Script:
run_macos_version_with_console.sh - Usage:
chmod +x ./run_macos_version_with_console.sh ./run_macos_version_with_console.sh - What it does:
- Detects arch (
arm64vsx86_64) and sets--targetaccordingly - Isolates artifacts per-arch via
CARGO_TARGET_DIR=target-macos-$ARCH - Unsets any stray cross-compile/link flags for a clean native build
- Uses all cores for build jobs and launches the app in terminal mode (
--mode terminal)
- Detects arch (
- Customize:
- Add app flags by appending to the final
-- ...section (e.g.,--threads 8) - Override logging:
RUST_LOG=info ./run_macos_version_with_console.sh
- Add app flags by appending to the final
macOS — GPU GUI (Metal)
- Script:
run_macos_version_with_gui.sh - Usage:
chmod +x ./run_macos_version_with_gui.sh ./run_macos_version_with_gui.sh - What it does:
- Same target/artifact isolation as console script
- Prefers Metal backend for
wgpu(WGPU_BACKEND=metal) - Builds with
--features guiand launches GUI mode (--mode gui) using--threads 8
- Customize:
- Tune threads: edit
--threads 8or setSCRIPTBOTS_MAX_THREADSenv - Troubleshoot rendering: you can add
--renderer-safeto the app args if you see a black canvas
- Tune threads: edit
macOS — Bevy renderer (Metal)
- Script:
run_macos_version_with_bevy.sh - Usage:
chmod +x ./run_macos_version_with_bevy.sh ./run_macos_version_with_bevy.sh - What it does:
- Selects the correct target triple (
aarch64-apple-darwinon Apple Silicon,x86_64-apple-darwinotherwise) - Isolates build artifacts per-arch and clears stray cross-compilation flags
- Forces the Metal backend, high-performance power preference, and Bevy feature flag (
--features bevy_render) - Launches ScriptBots with
--mode bevyand a default 8-thread budget (override withTHREADSenv)
- Selects the correct target triple (
- Customize:
- Retina tweaks: set
SB_WGPU_RES_SCALEto0.5or2.0before running - Lower CPU use:
THREADS=4 ./run_macos_version_with_bevy.sh - Add Bevy-specific CLI flags after
--, e.g.,./run_macos_version_with_bevy.sh -- --dump-semantic-png docs/rendering_reference/golden/bevy_default.png(CPU semantic raster; for real offscreen GPU captures use--dump-scene-png SCENE.toml)
- Retina tweaks: set
Windows — terminal console (MSVC)
- Script:
run_windows_version_with_console.bat - Usage:
- Double-click in Explorer, or run from a Developer PowerShell/Command Prompt:
run_windows_version_with_console.bat
- Double-click in Explorer, or run from a Developer PowerShell/Command Prompt:
- What it does:
- Uses MSVC target
x86_64-pc-windows-msvc - Isolates artifacts under
target-windows-msvc - Uses all cores for build jobs and launches terminal mode (
--mode terminal)
- Uses MSVC target
- Prereqs:
- Rust MSVC toolchain and Visual Studio Build Tools (Windows 11 SDK) installed
Windows — GPU GUI (D3D12/Vulkan)
- Script:
run_windows_version_with_gui.bat - Usage:
- Double-click in Explorer, or run from a Developer PowerShell/Command Prompt:
run_windows_version_with_gui.bat
- Double-click in Explorer, or run from a Developer PowerShell/Command Prompt:
- What it does:
- Same MSVC target/artifact isolation as console script
- Builds with
--features guiand launches GUI mode (--mode gui) using--threads 8
- Customize:
- Adjust threads by editing the
--threadsvalue; add app flags after--as needed (e.g.,--debug-watermark)
- Adjust threads by editing the
Windows — Bevy renderer (Vulkan/D3D12)
- Script:
run_windows_version_with_bevy.bat - Usage:
- Double-click in Explorer, or run from a Developer PowerShell/Command Prompt:
run_windows_version_with_bevy.bat
- Double-click in Explorer, or run from a Developer PowerShell/Command Prompt:
- What it does:
- Reuses the MSVC target (
x86_64-pc-windows-msvc) with isolated artifacts undertarget-windows-msvc - Sets high-performance WGPU hints (
WGPU_BACKEND=Vulkan,WGPU_POWER_PREFERENCE=high_performance) - Builds with
--features bevy_renderand launches the Bevy renderer (--mode bevy) using--threads 8
- Reuses the MSVC target (
- Customize:
- To force D3D12 instead of Vulkan: set
set WGPU_BACKEND=d3d12before running - Add Bevy-only flags (e.g.,
--dump-semantic-png,--dump-scene-png) after the final--in the script
- To force D3D12 instead of Vulkan: set
Notes (all platforms):
- The final
-- ...segment in each script passes flags to the application binary. You can add flags like--storage memory,--profile-steps 1000, or--det-check 200there. - To stream control API docs, ensure REST is enabled (default) and open
http://127.0.0.1:8088/docswhile the app runs.
Build for Web (experimental)
rustup target add wasm32-unknown-unknown
cargo check --target wasm32-unknown-unknown -p scriptbots-web
Windows quickstart (native)
- Install Rust (MSVC toolchain):
- Download
rustup-init.exeand select the MSVC target, or run in PowerShell:rustup default stable-x86_64-pc-windows-msvc rustup component add clippy rustfmt
- Download
- Install Visual Studio Build Tools (2022+):
- Select the "Desktop development with C++" workload (includes MSVC, Windows 10/11 SDK).
- Update GPU drivers (NVIDIA/AMD/Intel) to latest. Ensure D3D12 is available; Vulkan runtime optional.
- Build and run:
cargo run -p scriptbots-app - Troubleshooting: If linking fails with MSVC or SDK errors, re-run the VS installer to include the Windows 11 SDK and C++ toolset (v143+).
Windows via WSL2 (optional)
- Windows 11 with WSLg supports Linux GUI apps out of the box; GPUI rendering generally works, but performance may vary. If you see blank windows, update your GPU drivers and WSL kernel, then retry.
Feature flags & variants
scriptbots-appfeatures:ml→ enablescriptbots-brain-mlbrain-ft→ propagate the non-default pinned Frankentorch dependencies and code-firstFtBrainfamily throughscriptbots-brain-mlneuro→ enablescriptbots-brain-neurofast-alloc→ enable mimalloc as the global allocator for improved multithreaded performance- Example:
cargo run -p scriptbots-app --features neuro - Frankentorch compile, determinism, and benchmark acceptance is DSR-only through the pinned
rust_scriptbotsprofile; GitHub Actions and direct Cargo invocations are not acceptance evidence. - Note: default features enable
ml,neuro, andfast-alloc. To disable defaults, use--no-default-featuresand opt-in explicitly.
scriptbots-render:audio→ enable Kira-driven audio in the UI layer
scriptbots-index:gridis the implemented backend;rstarandkdcurrently enable dependencies only.- Example:
cargo build -p scriptbots-index --features rstar
scriptbots-brain-ml:candle,tract, andtchretain the dependency probes andml.placeholder.brain-ftexposesFtBrainConfig,FtBrainFamily, andFT_BRAIN_KIND.- The pinned Frankentorch
vector_to_parameterspath is not F32-safe, so FtBrain keeps a canonical flat F32 genome and materializes its layer slices without widening through F64. Resolution of that upstream gap and all compile/benchmark proof remain part ofbd-2z0.3.12.3.
Note: NeuroFlow and the native brain implementations are functional. Candle/Tract/tch inference and alternate spatial-index backends remain tracked implementation work. FtBrain now has a real code-first inference implementation, but is not accepted as complete until its pinned DSR evidence is green.
NeuroFlow runtime configuration (optional)
If built with the neuro feature, runtime toggles can be applied via env vars before launch:
SCRIPTBOTS_NEUROFLOW_ENABLED=true \
SCRIPTBOTS_NEUROFLOW_HIDDEN="64,32,16" \
SCRIPTBOTS_NEUROFLOW_ACTIVATION=relu \
cargo run -p scriptbots-app --features neuro
Valid activations: tanh, sigmoid, relu.
Commands cheat sheet
# Build the whole workspace
cargo build --workspace
# Run the UI shell
cargo run -p scriptbots-app
# Lint and format
cargo clippy --workspace --all-targets -- -D warnings
cargo fmt --all
# Run tests (as they land)
cargo test --workspace
# Build optional crates with features
cargo build -p scriptbots-index --features rstar
cargo build -p scriptbots-brain-ml --features candle # compile probe; inference is still a placeholder
Command-line options (scriptbots-app)
--mode {auto|gui|bevy|terminal}: select renderer. Defaults toautoand can be set viaSCRIPTBOTS_MODE.auto: choose GPUI, then Bevy, only when that backend is compiled and a real native graphical session is available; otherwise use the terminal.gui: require GPUI; an uncompiled feature or native window launch failure is returned to the caller.bevy: require the Bevy frontend and fail clearly unless built with thebevy_renderapplication feature.terminal: force emoji TUI.
--bootstrap-ticks N: explicitly runNscience ticks after seeding and before frontend launch (default0, so ordinary startup launches at tick zero).--dump-png <FILE>(GUI builds): write an offscreen PNG and exit (no UI). Pair with--png-size WxH.--png-size WxH(GUI builds): snapshot size for--dump-png(e.g.,1280x720).--debug-watermark: overlay a tiny diagnostics watermark in the render canvas.--renderer-safe: force a conservative paint path (useful for troubleshooting black canvas on some Windows setups).--threads N: cap simulation worker threads (overrides low-power defaults).--low-power: prefer lower CPU usage (equivalent to--threads 2unless--threadsis provided); also biasesautotoward terminal.--profile-steps N: headlessworld.step()profiling without persistence.--profile-storage-steps N: headless profiling with selected storage mode.--storage-thresholds t,a,e,m: override flush thresholds (tick, agent, event, metric).--profile-sweep N: run a sweep of configurations for profiling and print a summary.--auto-tune N: quick sweep to pick threads/thresholds for the chosen storage, then continue.--det-check N: run determinism self-check (1-thread vs N-threads summaries comparison).--set PATH=VALUE: dotted-path configuration override in TOML syntax, repeatable (e.g.,--set world_width=800 --set neuroflow.enabled=true; string values use TOML quotes). Configuration layers apply defaults →--configfiles (in order) → environment → CLI; every applied layer appends a kind-tagged content digest to the run manifest'sscenario.ordered_config_layer_digests, and any field where one explicit layer displaced another is recorded in the manifest'sconfig_overrides.--quality TIER: visual quality shortcut for--set render.quality=TIER, validated at parse time (auto|potato|low|medium|high|ultra). The unifiedrender.*settings (quality tier, post stack bloom/vignette/fog/AA, day/night cycle, TUI theme, accessibility palette) are consumed by every frontend;Nonevalues defer to per-tier frontend defaults.--dump-png FILE+--png-size WxH(GUI builds): write an offscreen PNG and exit.--dump-semantic-png FILE(Bevy builds): write the CPU semantic projection raster and exit. This is a semantic reference only — it does NOT exercise the GPU pipeline (formerly--dump-bevy-png, renamed so it can never be mistaken for a GPU capture).--dump-scene-png SCENE.toml(Bevy builds): render a scene manifest through the REAL offscreen Bevy GPU pipeline and write capture PNGs + provenance JSON + a JSON scene log undercaptures/<scene>/. Golden workflow:RUST_REGEN_GOLDEN=1blesses new goldens undercrates/scriptbots-app/tests/scenes/goldens/<scene>/; an existing golden is compared with per-channel/perceptual thresholds (mismatch writes a<name>.diff.pngheatmap and fails); a missing golden is an explicit failure with regeneration instructions, never an auto-bless.SCRIPTBOTS_CAPTURE_CORRUPT=1enables the alarm-test corruption mode (blacked-out lighting) used to prove a broken pipeline fails the harness.--storage {file|memory}: select the FrankenSQLite target.fileexclusively reservesSCRIPTBOTS_STORAGE_PATHor a fresh generated run path and refuses existing databases or stale sidecars;memoryopens volatile:memory:through the same engine.--recover-storage FILE: exclusively reopen a validated existing ScriptBots run, replay/finalize its durable outbox, print the admitted/applied/durable watermarks, and exit. This repairs persistence only; it does not reconstruct the in-memory world or resume simulation ticks.- Auto-pause (any renderer):
--auto-pause-below COUNT(orSCRIPTBOTS_AUTO_PAUSE_BELOW) pauses when population ≤ COUNT--auto-pause-age-above AGE(orSCRIPTBOTS_AUTO_PAUSE_AGE_ABOVE) pauses when any agent’s age ≥ AGE--auto-pause-on-spike(orSCRIPTBOTS_AUTO_PAUSE_ON_SPIKE=true) pauses on first spike hit event
Environment variables (quick reference)
RUST_LOG— logging filter (e.g.,info,trace,scriptbots_core=debug).RAYON_NUM_THREADS— set simulation thread pool size whenparallelis enabled.SCRIPTBOTS_MODE—auto|gui|bevy|terminal(renderer selection).SCRIPTBOTS_FORCE_TERMINAL/SCRIPTBOTS_FORCE_GUI— override Auto-mode renderer detection (1|true|yes); explicit--moderemains authoritative.SCRIPTBOTS_BOOTSTRAP_TICKS— explicit pre-frontend science ticks (default0, equivalent to--bootstrap-ticks; set a nonzero value only when an intentional warmup is part of the run).SCRIPTBOTS_TERMINAL_HEADLESS— render TUI to an in-memory buffer for CI smoke tests.SCRIPTBOTS_TERMINAL_HEADLESS_FRAMES— number of frames to render in headless mode (default 12; max 360).SCRIPTBOTS_TERMINAL_HEADLESS_REPORT— file path to write a JSON summary from a headless run.SCRIPTBOTS_MAX_THREADS— preferred maximum thread budget; core will cap Rayon to min of CPUs and this value (used unlessRAYON_NUM_THREADSis already set).SCRIPTBOTS_TERMINAL_EMOJI— force emoji mode1|true|yes|onor disable with0|false|off|no.SCRIPTBOTS_RENDER_SAFE— force conservative rendering path in GUI mode (also enabled by--renderer-safeor--low-power).SCRIPTBOTS_RENDER_WATERMARK— overlay a tiny diagnostics watermark in the GUI canvas (also enabled by--debug-watermark).SCRIPTBOTS_RENDER_QUALITY—auto|potato|low|medium|high|ultraforrender.quality(the--qualityCLI flag outranks it).- Legacy render knobs
SB_WGPU_TONEMAP|EXPOSURE|BLOOM|BLOOM_THRESH|BLOOM_INTENSITY|VIGNETTE|FOG|FOG_COLOR|FXAAandSCRIPTBOTS_TERMINAL_PALETTEare mapped onto the typedrender.*schema at startup with one INFO log per applied mapping; the canonical typedSCRIPTBOTS_RENDER_*variables and CLI flags outrank them. SCRIPTBOTS_CONFIG_OVERRIDES— inline TOML document merged as the most-general environment configuration layer (e.g.,world_width = 1000). Beats--configfiles, loses to the typedSCRIPTBOTS_*variables and to every CLI flag; malformed content fails startup closed. Each applied layer appends a kind-tagged digest to the manifest, and cross-layer displacements land in the manifest'sconfig_overrides.SCRIPTBOTS_RNG_SEED— environment-layer RNG seed;--rng-seedoutranks it as a distinct CLI layer with its own provenance.SCRIPTBOTS_NEUROFLOW_ENABLED—true|false.SCRIPTBOTS_NEUROFLOW_HIDDEN— comma-separated hidden sizes (e.g.,64,32,16).SCRIPTBOTS_NEUROFLOW_ACTIVATION—tanh|sigmoid|relu.SCRIPTBOTS_CONTROL_REST_ADDR— REST bind address (default127.0.0.1:8088).SCRIPTBOTS_CONTROL_SWAGGER_PATH— Swagger UI path (default/docs).SCRIPTBOTS_CONTROL_REST_ENABLED—true|false.SCRIPTBOTS_CONTROL_MCP—disabled|http(defaulthttp).SCRIPTBOTS_CONTROL_MCP_HTTP_ADDR— MCP HTTP bind address (default127.0.0.1:8090).- Control-server environment is validated before startup side effects. Malformed or non-Unicode control values fail closed;
https://in either MCP transport/address setting is rejected because the embedded MCP listener is plaintext HTTP rather than silently claiming TLS. SCRIPTBOTS_STORAGE_PATH— optional new-run FrankenSQLite file path. Without it, ScriptBots creates a uniqueruns/scriptbots-<unix-ms>-<pid>.sqlite. In either case the app reserves the path with create-new semantics and refuses an existing database or stale-wal,-shm,-journal,-wal-fec, or lock sidecar. The selected path is printed asRun database: ...; save that exact value for later reads and exports.SCRIPTBOTS_RECOVER_STORAGE— existing file-backed run to repair and finalize, equivalent to--recover-storage FILE. Recovery exits after persistence repair; it does not resume the simulation. The core science checkpoint is a separate persistence-disabled API, while application run-bundle discovery and resume remain roadmap work.
Dual-window mode (GUI)
- ScriptBots opens two GPUI windows as one transactional launch: a canvas window rendering the world and a HUD window with controls, charts, and inspector. If either window cannot be created (for example because of window-manager or remote-session limits), ScriptBots terminates the partial application lifetime and returns an actionable error; it never silently changes the requested layout. GPUI uses
QuitMode::LastWindowClosed, and closing either member of the paired session closes the application rather than leaving one orphaned window or a hidden process alive. - This is lifecycle hardening plus interim double-drive containment: the HUD is the only current GPUI simulation driver and the world window is read-only. It is not the final ownership fix—the renderer still owns scientific time and GPUI command draining remains incorrect. The
HostCoremigration owns both remaining defects and the permanent exactly-one-driver proof.
Simulation overview
Deterministic, staged tick pipeline (six seeded, domain-separated RNG streams; explicit staged ordering):
- Aging and scheduled tasks
- Food respawn/diffusion
- Sense (spatial index snapshot)
- Brain tick
- Actuation (double-buffered state)
- Food intake/sharing (deterministic reductions)
- Combat and death (queued → commit)
- Reproduction (mutation/crossover) in stable order
- History and persistence projection (batched to the bounded FrankenSQLite worker)
- Preserve the final persistence tail and reset transient runtime flags
Design principles & determinism
- No undefined behavior: workspace lints flag
unsafe; the remaining platform/environment boundary blocks are explicit and reviewable. - Explicit order of effects: floating-point reductions and removals are staged. Before each successful tick, the dense SoA is normalized to ascending stable
AgentUid; stable compaction and monotonic insertion preserve that order through death and spawn commits. Physical slot allocation therefore cannot choose reduction, neighbor, parent, or child priority. - Restorable domain and agent-keyed RNG state: the world owns six independently derived, versioned streams—Environment, Food, Population, Lineage, Mutation, and Crossover—plus a versioned agent-substream protocol and UID-keyed continuation counters. Agent operations derive isolated streams from the root seed, stable
AgentUid, operation tag, and agent-local ordinal; offspring operations derive from ordered parent UIDs plus the primary parent's local birth ordinal. The global demographicAgentIdentity::birth_ordinalremains a separate run-wide lifecycle sequence and is never used as the offspring RNG identity. - Transactional stochastic ownership: a reproduction attempt claims its parent's local attempt continuation, and an admitted child claims that parent's local birth continuation. Child body, runtime, brain genome, and evaluator-state operations each receive one distinct derived stream. A rejected transaction restores the exact counter preimage together with energy, reproduction progress, population inserts, and other staged state, so a failed child cannot consume randomness that a retry or unrelated agent would observe.
- Feature-gated parallelism:
scriptbots-coredefaults toparallel(Rayon), while web builds disable it for single-thread determinism. - Completed-boundary outcome and admission seam:
WorldStateowns deterministic accumulators plus a payload-freeOpen/Pending/Sealedboundary marker; it no longer owns a sink, retry payload, acknowledgement error, or admission watermark. A one-lifetimePersistenceAdmissionSessionowns those external concerns. Its step APIs stage the exact immutableArc<PersistenceBatch>before returning, retain that same allocation across definite and indeterminate failures, and seal the world only after acknowledgement. DirectWorldStatestepping is intentionally limited to persistence-disabled worlds. Application-owned drivers route TUI, GPUI, Bevy, headless, profiling, and WASM ticks through the matching session without exposing session state to renderers. Completed population faults still travel beside the fullStepOutcome; the clock-free traced outcome keeps the same boundary, and profiled session stepping retains the reviewed v2 timing contract. The hidden playback transport queue is also retired: application drains return one orderedVec<ControlCommand>, while core returns normalized playback as an explicit driver-owned disposition.
Canonical digest and first-divergence trace
WorldState::world_digest_v1()emits the exactscriptbots.world-digest.v1.6/codec-6 science-state wire. Every agent transition is canonicalized by stableAgentUid, so physical slot/dense allocation is no longer science state and the temporary V1.1 execution-order lane is retired. The aggregate covers stable-UID agent bodies, brain genomes/evaluator coverage, food, terrain/hydrology, all six restorable RNG-domain checkpoints with per-domain diagnostics, the exactAgentSubstreamProtocolV1, UID-orderedAgentRngCounterStateV1rows carryingAgentRngCountersV1, global identity counters, legacy factory-state digests or protocol adapter semantic identities, the selected locomotion model and remaining scientific configuration, active effects, derived transition caches, and open ancestry origins. Product-admitted MLP, DWRAON, and Assembly adapters therefore report complete construction-semantics coverage. Operational rendering, analytics, persistence-admission policy, host receipts, and backend error prose do not enter the science digest.WorldState::checkpoint_v1()captures the bounded canonicalscriptbots.world-checkpoint.v1.3/codec-5postcard+blake3-v5core science envelope only when persistence is disabled and the world is at an open completed boundary. It persists the exact agent-substream protocol, selected locomotion model, and one counter object per stable UID. Decode and restore reject missing, duplicate, reordered, mismatched, or incompatible counter/protocol state before constructing any evaluator or agent.WorldCheckpointV1::decode()also rejects oversized, trailing, noncanonical, checksum-invalid, and semantically malformed bytes;WorldState::restore_checkpoint_v1()allocates fresh physical handles from stableAgentUidorder and reconstructs full genome/evaluator state through a caller-prepared exactBrainRegistry. The registry's complete roster, including its next allocation key and every protocol adapter's exactBrainAdapterIdentityV1, must match before reconstruction. The identity is a family-authored semantic attestation, not executable-byte authentication: construction/evaluation changes must change it, while payload interpretation changes must additionally bump the family schema/codec. The checkpoint never deserializes executable adapters and does not claim to restore storage ownership, analytics/history, configuration audit provenance, UI/render state, or an application run bundle. Its unkeyed BLAKE3 checksum detects corruption and canonical drift; it is not authentication.WorldState::step_traced()is an opt-in, clock-free diagnostic API usingscriptbots.world-step-trace.v1.6/codec-6. It records exactly six semantic checkpoints—Sense, Brains, Actuation, Food, DeathCleanup, and Population—with separate world, ordered deferred-work, pending output-tail, and resource-ledger hashes. The embedded world and transition lanes bind the same agent-substream protocol, UID-ordered counters, selected locomotion model, and queued offspring identities as the final digest. The trace has its own aggregate hash, typed capture failures, andvalidate_contract()enforcement for schema, cardinality, order, ticks, coverage, digest shape, and nested aggregate hashes.- Both V1 and trace validation prove protocol self-consistency, not artifact authenticity. Their FNV-1a64 hashes are diagnostic first-divergence aids, not signatures or collision-resistant attestations; a durable run bundle will require separate provenance/authentication.
- DeathCleanup and Population retain the ordered queue state immediately before consumption, while the final completed-boundary V1 and resource report prove traced/untraced parity after finalization. The literal six-point golden is enabled only by the
SCRIPTBOTS_WORLD_DIGEST_GOLDEN=1environment guard in its pinned DSR test lane, so ordinary--all-featurestests remain portable; there is no product CLI or replay-file claim yet.
Reproducible runs (seed control)
- Set a fixed seed in config:
rng_seed = <u64>. At runtime you can apply via REST:{ "rng_seed": 42 } - The root seed deterministically derives all six domain streams and every stable agent/offspring substream. The canonical launch manifest is
scriptbots.run-manifest.v3.3: it records the six-domain checkpoint, exactAgentSubstreamProtocolV1, and UID-orderedAgentRngCounterStateV1launch rows. Once the tick-zero V1.6 start digest is bound, the bootstrap form isscriptbots.run-manifest.v3.5; it must bind the same root, protocol, counters, registry semantics, locomotion model, andWorldDigestV1evidence rather than describing a different launch state. Neither form collapses the domains back into one ambiguous global stream. - The
bd-1kxdkeyed-substream schema and thebd-2i1V1.6 locomotion contract are closed with pinned DSR evidence. The reviewed exact-class full performance baseline was promoted byte-for-byte in39fc2f0, and the final same-class comparison passed in DSR0.1.0-bd2i1-perf-compare-quiet1.20260716T160817Z.bd-hiv1remains open for the separate movement-noise and spike-speed consumers that must use legacy wheel effort rather than physical displacement. - For CPU thread control during profiling, prefer the standard
RAYON_NUM_THREADSenv var.
Data model & spatial indexing
- SoA layout: agents use cache-friendly columns (
AgentColumns) for fast scans during sense/actuation. - Two identity layers: slotmap-backed
AgentIdprevents stale references; monotonicAgentUidis the stable scientific identity used by digests, lineage, replay, and persistence. - Spatial index: uniform hash grid. Sense builds a read-only snapshot; the declared
rstar/kdfeatures do not yet provide alternate implementations.
Sensors & outputs
- Sensors: multi-eye vision cones (angular), smell/sound/blood channels with attenuation, temperature discomfort, and clock/age cues.
- Outputs: wheel velocities integrated by the exact legacy two-rotation model by default, with conventional differential drive selectable for experiments; color/indicator pulses, spike length easing, give intent (altruistic food sharing), boost control, and sound output.
- Mapping: outputs drive physics and side-effects (e.g., spike damage scales with spike length and speed) and are logged for analytics.
Brains & evolution
- Brain trait with
tick/mutate/crossover; implementations include MLP and DWRAON by default, plus experimental Assembly. - Brain registry: per-run registry attaches runners by key, enabling hybrid populations and runtime selection. Random spawns draw from
BrainRegistry::random_keyfor mixed-species runs; sexual crossover is gated to same-kind brains (species barrier). Brains can optionally expose activation snapshots for visualization. - Genome & genetics: genomes capture topology/activations; mutation/crossover create hybrid births with lineage tracking and tests.
- NeuroFlow (optional): deterministic CPU MLP with runtime toggles; seed-stable outputs verified in tests.
Environment: food, terrain, temperature
- Food dynamics: configurable growth, decay, diffusion, and fertility capacity; speed-based intake and reproduction bonuses mirror legacy behavior.
- Topography: tile-based terrain/elevation influence fertility and movement energy (downhill momentum/energy costs).
- Temperature: gradient and per-agent preference drive discomfort drains; exposed in config and analytics.
- Closed worlds & seeding: enforce closed ecosystems; maintain population floors and scheduled spawns.
Procedural maps (WFC sandbox; in progress)
- The core ships a rule-based Wave Function Collapse (WFC) generator that produces deterministic terrain (
TerrainLayer) and optional fertility/temperature fields from a tileset spec. This enables quick scenario bootstrapping and repeatable experiments. - Status and usage live in the plan doc; upcoming surfaces include REST/CLI endpoints to generate/apply artifacts. The hydrology system builds atop terrain for dynamic water flows (see below).
Hydrology snapshot (experimental)
- Runtime hydrology state models per-cell flow direction, accumulation, basins, and a water depth field. Use
GET /api/hydrologyfor a snapshot with:width,height,total_water_depth,mean_water_depth, flooded cell counts with thresholds- Arrays:
water_depth,flow_directions(N/S/E/W/-),basin_ids,accumulation,spill_elevation
- CLI:
scriptbots-control hydrologyprints a summary (ratios, thresholds, array sizes). Hydrology integrates with terrain and is deterministic per seed.
Combat & mortality analytics
- Spikes: damage scales with requested spike length and agent speed; collision resolution is staged for determinism.
- Carcass sharing: meat distribution honors age scaling and diet tendencies; events persisted for analysis.
- Analytics: attacker/victim flags (carnivore/herbivore), births/deaths, hybrid markers, age/boost tracking, and per-tick summaries feed FrankenSQLite plus the immutable HUD analytics snapshot.
Rendering & UX
- GPUI window, HUD, and canvas renderer for food tiles and agents (circles/spikes). The dual-window HUD and simulation canvas launch transactionally under
QuitMode::LastWindowClosed; closing either paired window ends the session, so a launch/close failure cannot leave a degraded or orphaned UI. The world window is read-only while the HUD temporarily remains the sole driver pendingHostCore. - Camera controls: pan/zoom; keyboard bindings for pause, draw toggle, speed ±.
- Overlays: selection highlights, diagnostics panel; charts and advanced overlays are staged in the plan.
- Functional search: the settings panel includes a live search bar that filters parameters across all categories via a centralized filter, making it fast to find and tweak knobs.
- Inspector: per-agent stats and genome/brain views (scoped to plan milestones); mutation-rate adjusters (±) for primary/secondary let you tweak an agent’s evolution parameters live.
- Optional audio via
kira(featureaudio).
GPUI performance & diagnostics
- Adaptive GPU adapter selection; viewport culling for terrain; chart decimation; batched path rendering to reduce draw calls.
- Troubleshooting flags:
--renderer-safe(conservative paint path) and--debug-watermark(tiny on-canvas badge) help isolate rendering issues.
Accessibility & input
- Colorblind-safe palettes (deuteranopia/protanopia/tritanopia) and a high-contrast mode; UI elements and overlays respect palette transforms.
- Keyboard remapping with conflict resolution and capture mode; discoverable bindings in the HUD.
- Narration hooks prepared for future screen-reader integration; toggles surfaced in the inspector.
Renderer abstraction
- The app selects GPUI, Bevy, or terminal renderers via
--mode {auto|gui|bevy|terminal}(subject to build features). Every frontend consumes the same world and immutable analytics snapshots.
Keyboard shortcuts (GUI)
- Playback:
spacepause/resume,+/-speed up/down,ssingle-step - Views:
dtoggle drawing,ftoggle food overlay,Ctrl+Shift+Otoggle agent outlines - Spawning:
aadd crossover agents,q/hspawn carnivore/herbivore - Persistence safety: a spawn shortcut is refused after the current tick is admitted under the selected storage guarantee; advance to the next open tick boundary before spawning. An unresolved admission is reported separately and must be resolved by retrying the exact retained batch before either the world or an external arrival can advance. Both typed reasons are logged without consuming simulation RNG.
- World:
ctoggle closed environment,ofollow oldest,sfollow selected - Accessibility:
pcycle color palettes (with keyboard rebinding support)
Audio system
- Optional
kira-backed mixer (featureaudio) with event-driven cues (births, deaths, spikes) and accessibility toggles. - Channels planned for ambience/effects; platform caveats apply on Linux/WSL2. Audio is disabled in wasm; use Web Audio API from JS if needed.
Terminal mode
The implemented Ratatui frontend provides an emoji-rich dashboard and headless snapshot mode. Automatic selection chooses it only before launch; an explicit GPUI/Bevy request or a native window launch failure is surfaced rather than silently changing products.
Terminal-only mode
-
Force the emoji TUI renderer (useful on headless machines):
SCRIPTBOTS_MODE=terminal cargo run -p scriptbots-app -
Auto selection:
SCRIPTBOTS_MODE=auto(default) chooses a compiled native renderer only for a real native graphical session and otherwise starts the terminal (including macOS SSH sessions). -
Auto-mode policy overrides:
SCRIPTBOTS_FORCE_TERMINAL=1→ choose terminal in Auto mode even when a display server is present.SCRIPTBOTS_FORCE_GUI=1→ require compiled GPUI in Auto mode even if no display variables are set; failures remain visible.
-
CI/headless smoke runs can bypass raw TTY requirements by setting
SCRIPTBOTS_TERMINAL_HEADLESS=1, which drives the renderer against an in-memory buffer for a few frames. -
Emoji mode (terminal renderer):
- Defaults ON when a modern UTF‑8 terminal is detected; press
eto toggle at runtime. - Force enable via env:
SCRIPTBOTS_TERMINAL_EMOJI=1|true|yes|on; force disable with0|false|off|no. - Heuristic: enabled if
TERMis notdumb/linux/vt100, locale containsutf-8|utf8, andCIis unset. - Emoji mappings: terrain
🌊/💧/🏜/🌿/🌺/🪨(lush swaps:🐟,🌴,🌾, barren🥀); agents single🐇/🦝/🦊, small groups🐑/🐻/🐺, large cluster👥, boosted🚀, spike peak⚔(underline). Heading arrows remain for single agents when available. - If emojis render as tofu/misaligned, install an emoji-capable font (e.g., Noto Color Emoji) or toggle off with
e.
- Defaults ON when a modern UTF‑8 terminal is detected; press
-
Narrow symbols mode: press
nto switch to width-1 friendly symbols while keeping emoji colors off-background; helpful for strict terminals/alignment.
Keybinds: space (pause), +/- (speed), s (single-step), b (toggle metrics baseline), S (save ASCII screenshot), e (emoji), n (narrow symbols), x (expanded panels), ?/h (help), q/Esc (quit). The terminal HUD shows tick/agents/births/deaths/energy, Insights (rolling metrics), Mortality panel, Brains leaderboard, recent events log, and an emoji world mini-map. The layout is responsive and auto-expands panels on wider terminals; press x to toggle. Screenshots saved via S are written under screenshots/frame_<tick>.txt.
Storage & analytics
- One embedded engine: ScriptBots uses the public
fsqlitefacade from FrankenSQLite withversion = "=0.1.16", pinned to immutable revisione536d7f8ca102b3eb5236bef48514582379f9346athttps://github.com/Dicklesworthstone/frankensqlite. The current dependency enablesnativewith default features disabled and provides create-free existing-file open plus expected-identity verification before recovery can read or mutate database bytes. - Two storage targets:
--storage fileexclusively reservesSCRIPTBOTS_STORAGE_PATHor a generatedruns/scriptbots-<unix-ms>-<pid>.sqliteand prints the selected run database; it refuses reuse or stale sidecars.--storage memoryopens volatile:memory:through the same implementation. - Explicit interrupted-run recovery:
--recover-storage FILE(orSCRIPTBOTS_RECOVER_STORAGE) is the only application path that opens an existing run database for mutation. It holds the OS writer lease, binds recovery to the identity of the already-open VFS handle, verifies the exact structural schema fingerprint plus the supported migration sequence and persistence invariants, and refuses missing, replaced, unrelated, symlink, and multiply-linked files before replaying admitted-but-unapplied outbox rows and finalizing applied rows. It prints the resulting watermarks and exits. This is persistence repair, not world resume. - Thread-confined, single-writer connection:
fsqlite::Connectionis deliberately!Send + !Sync. The storage worker creates, uses, explicitly closes, and drops its connection on that worker thread. File writers hold a nonblocking OS advisory lease on a persistent companion lock file; process-local path/inode tracking is only defense in depth. No connection-owning value is shared throughArc<Mutex<_>>. - Bounded admission, distinct proof levels: persistence batches enter a bounded queue. Configurable
StorageDeadlinesbound startup, admission-gate, command-enqueue, receipt, flush, and shutdown acknowledgement waits, but cannot cancel a database call already executing on the owner thread or bound the supervised reaper. Validation, closed-gate, queue-send, and rolled-back outbox failures are definitelyNotAdmitted; the external admission session retains the exact completed batch and acknowledgement fault while the world's payload-freePendingmarker blocks later science ticks until explicit retry succeeds. A lost or timed-out acknowledgement remains typed asIndeterminateat the world boundary, but retrying the unchanged canonical payload is idempotent and reuses its stable batch ID; a conflicting payload is rejected by its BLAKE3 identity. Timed-out shutdown retains the exact pending receipt and worker handle for retry; dropping the controller hands both to an independent supervised reaper rather than abandoning connection ownership.submit_with_receiptreturns the batch ID after the exact payload enters the worker outbox and reportsDurablefor a file database orCommittedVolatilefor memory. That r
