Development

Prerequisites

Tool Version Purpose
Python ≥ 3.11 Backend (primary)
Node.js ≥ 20 Frontend build
npm ≥ 10 Frontend deps
Docker + Compose any recent Dev stack
Rust ≥ 1.93 Backend (optional, in-sync)
mosquitto-clients optional MQTT debugging

Quick start (frontend-only, no Rust build)

The fastest way to develop the UI is against the mock agent simulator.

# Terminal 1 — MQTT broker + mock agents
docker compose -f compose.dev.yaml up -d

# Terminal 2 — Vite dev server (hot-reload on http://localhost:3000)
cd frontend
npm install
npm run dev

The mock simulator (scripts/mock-agents.mjs) publishes realistic MQTT events: - 6 agents: main-actor, monitor-agent, data-fetcher, weather-agent, ml-classifier, nautilus-agent, udx-agent - Heartbeats every 5 s - Chat replies every 4 s - Occasional alerts (30% chance every 8 s) - Dynamic agent spawns every 20 s (20% chance)

The Vite dev server proxies /api/, /ws, and /mqtt to the local ports exposed by compose.dev.yaml.

Python-first dev mode

For day-to-day development, the repo can default to the Python backend while exposing the same frontend-facing API contract (which the optional Rust backend mirrors).

# Terminal 1 — MQTT broker
docker compose -f compose.dev.yaml up -d mosquitto

# Terminal 2 — Python backend, REST on :8080
WACTORZ_DEV_MODE=1 ./run.sh

# Terminal 3 — frontend SPA
cd frontend && npm install && npm run dev

In this mode: - run.sh defaults to the Python backend unless WACTORZ_BACKEND overrides it. - The Python backend defaults to INTERFACE=rest and PORT=8080. - The primary frontend is frontend/index.html, not the legacy monitor.html.

Stop mock stack

docker compose -f compose.dev.yaml down

Full stack (Rust backend, optional in-sync)

# Terminal 1 — support services
docker compose up -d mosquitto dashboard   # or 'docker compose up -d'

# Terminal 2 — Rust backend
cd rust
RUST_LOG=wactorz=debug cargo run --bin wactorz -- \
    --mqtt-host localhost \
    --llm-provider anthropic \
    --llm-api-key "$LLM_API_KEY"

# Terminal 3 — frontend dev server
cd frontend && npm run dev

Project structure

wactorz/
├── rust/                          Rust workspace
   ├── Cargo.toml                 workspace manifest
   ├── Dockerfile                 multi-stage: builder  runtime
   └── crates/
       ├── wactorz-core/        Actor trait, registry, messages
       ├── wactorz-agents/      All concrete agents
       ├── wactorz-mqtt/        MQTT client + topic helpers
       ├── wactorz-interfaces/  REST, WebSocket, CLI
       └── wactorz-server/      Binary entry point

├── frontend/                      Vite + TypeScript + Babylon.js SPA
   ├── src/
      ├── main.ts                App bootstrap, wires all components
      ├── types/agent.ts         Shared TypeScript types
      ├── mqtt/MQTTClient.ts     MQTT client, event routing
      ├── io/
         ├── IOManager.ts       Sends messages to io/chat
         ├── IOBar.ts           Input bar (history, multiline, @mention)
         ├── AgentImageGen.ts   DiceBear + Gemini avatar generation
         └── VoiceInput.ts      Web Speech API integration
      ├── scene/
         ├── SceneManager.ts    Babylon.js lifecycle, theme switching
         ├── themes/            GraphTheme, GalaxyTheme, GraveTheme,
                                 CardBabylonTheme, ThemeBase
         ├── nodes/             GraphNode, PlanetNode, GraveNode,
                                 AgentNodeBase, CardBabylonNode
         └── effects/           AlertEffect, HeartbeatEffect,
                                  MessageEffect, SpawnEffect
      └── ui/
          ├── ChatPanel.ts       Per-agent chat threads
          ├── SocialDashboard.ts Instagram/Twitter-style card grid
          ├── CardDashboard.ts   Minimal HTML card grid
          ├── ActivityFeed.ts    Collapsible MQTT event log
          ├── MentionPopup.ts    @mention autocomplete
          ├── AgentHUD.ts        Top-left agent count HUD
          └── ThemeSwitcher.ts   Theme toggle buttons
   ├── public/
      ├── favicon.svg
      ├── site.webmanifest
      └── robots.txt
   ├── index.html                 All CSS is inline here (no external CSS files)
   ├── vite.config.ts
   └── tsconfig.json

├── infra/
   ├── nginx/
      ├── nginx.conf             Full Docker mode
      └── nginx-native.conf      Native binary mode
   ├── mosquitto/mosquitto.conf
   ├── fuseki/                    Apache Jena Fuseki
   └── homeassistant/             HA configuration

├── scripts/
   ├── deploy.sh                  Build + rsync + restart wizard
   ├── mock-agents.mjs            Dev-mode mock MQTT simulator
   ├── package-full-release.sh    Build full zip release
   ├── package-native.sh          Build native binary tar.gz
   └── build-native.sh            Build binary on the target host

├── systemd/wactorz.service      systemd unit template
├── compose.yaml                   Full Docker stack
├── compose.dev.yaml               Dev stack (mosquitto + mock only)
├── compose.native.yaml            Native binary stack
├── .env.example                   Annotated config template
└── docs/                          This documentation

Rust development

cd rust

# Check (fast — no codegen)
cargo check

# Build debug
cargo build --bin wactorz

# Build release
cargo build --release --bin wactorz

# Run with debug logging
RUST_LOG=wactorz=debug cargo run --bin wactorz

# Lint
cargo clippy -- -D warnings

# Format
cargo fmt

Backend parity proof

The repo now carries a shared backend parity contract for the core supervisor semantics that both runtimes claim to implement.

# Run language-specific tests plus the cross-runtime parity diff
make test

# Run only the parity proof
make parity

# Generate coverage artifacts used by CI
python -m pip install -r requirements-dev.txt
make coverage

The shared fixture lives at tests/parity_fixtures/backend_supervisor_parity.json. Both the Python harness and the Rust harness execute those same scenarios and must produce the same normalized JSON output for the build to pass.

Pre-commit

python -m pip install -r requirements-dev.txt
make precommit-install
make precommit-run

The configured hooks run: - Python unit tests - Rust backend parity test - Cross-backend parity diff - Frontend type-checking

Cross-compile for Linux from macOS

# Install target
rustup target add x86_64-unknown-linux-gnu

# Install linker (Homebrew)
brew install FiloSottile/musl-cross/musl-cross

# Build
CARGO_TARGET_X86_64_UNKNOWN_LINUX_GNU_LINKER=x86_64-linux-musl-gcc \
cargo build --release --target x86_64-unknown-linux-gnu --bin wactorz

Alternatively, use Docker buildx (no linker needed):

docker buildx build --platform linux/amd64 --tag wactorz:local --load ./rust

Frontend development

cd frontend

# Install deps
npm install

# Dev server (hot-reload, http://localhost:3000)
npm run dev

# Type-check only
npm run typecheck   # or: npx tsc --noEmit

# Production build
npm run build
# → static/app/

# Preview production build locally
npm run preview

Vite dev proxy

// vite.config.ts
proxy: {
  "/api":  { target: "http://localhost:8080", ... },
  "/ws":   { target: "ws://localhost:8081",  ws: true },
  "/mqtt": { target: "ws://localhost:9001",  ws: true },
}

This means in dev, window.location.host resolves to localhost:3000, and the MQTT URL is automatically ws://localhost:3000/mqtt → proxied to Mosquitto.

Adding a new UI theme

  1. Create frontend/src/scene/themes/MyTheme.ts extending ThemeBase
  2. Add a node type in frontend/src/scene/nodes/
  3. Register in SceneManager.ts: typescript case "my-theme": this.activeTheme = new MyTheme(this.scene, ...); break;
  4. Add a button in index.html and ThemeSwitcher.ts

Debugging tips

MQTT messages not arriving in the browser

# Subscribe to all topics from the terminal
mosquitto_sub -h localhost -p 1883 -t '#' -v

# Or check the mock is publishing
docker compose -f compose.dev.yaml logs mock-agents

Rust actor not responding

RUST_LOG=wactorz=debug cargo run --bin wactorz
# Look for: "[agent-name] handle_message" or heartbeat logs

TypeScript errors

cd frontend && npx tsc --noEmit

MQTT URL wrong in browser

Check browser console — the MQTTClient logs its connection URL. In dev it should be ws://localhost:3000/mqtt. Override with VITE_MQTT_WS_URL in .env if needed.