Spanda

Spanda Concurrency Model

Spanda uses a deterministic, cooperative concurrency model built around tasks, agents, messages, channels, and events.

Core model

Tasks and scheduler

Tasks are periodic units of work.

task perception high every 50ms {
  let scan = lidar.read();
}

Priority levels:

Priority affects execution order when multiple tasks are due on the same tick. critical tasks execute first.

Tasks may also omit every and use the runtime default period:

task SafetyMonitor critical {
  emergency_stop();
}

Per-task budgets are supported:

task mapping every 100ms {
  budget {
    battery <= 10%;
    cpu <= 20%;
  }
}

Async / await

Async module functions return Future<T> in the type checker and are resolved by await in runtime execution.

export async fn detect_objects() -> Pose {
  return pose(x: 0.0 m, y: 0.0 m, theta: 0.0 rad);
}

let result = await detect_objects();

Spawn, channels, and select

spawn returns a TaskHandle<T> that can be joined explicitly. Fire-and-forget spawn statements still queue work for completion at the end of the current behavior.

let h = spawn planning();
let plan = join(h);

spawn telemetry();  // fire-and-forget

Channels provide typed message passing inferred from first send.

let ch = channel();
send(ch, 42);
select {
  recv(ch) => {
    wheels.stop();
  }
};
spawn planning();

Parallel and join

Use parallel for cooperative concurrent call orchestration with deterministic completion.

parallel {
  let perception = spawn detect_objects();
  let localization = spawn localize();
  let map = spawn update_map();
};

let fused = _parallel;

Each branch runs in an isolated environment. let bindings and spawned handles are joined deterministically and exposed as _parallel (ParallelResults).

parallel {
  detect_objects();
  localize();
  update_map();
};

Use join to resolve a Future<T> or TaskHandle<T> handle explicitly.

let f = detect_objects_async();
let result = join(f);

let h = spawn perception();
let scan = join(h);

Agents and multi-agent communication

Agents are first-class runtime entities with:

Agent-to-agent links are declared with typed channels in robot declarations.

Distributed and simulation execution

Spanda communication abstractions are transport-backed and usable across local simulation and distributed deployments.

Safety isolation guidance

For safety-critical loops:

This ensures the scheduler services safety tasks before lower-priority workloads on each deterministic tick.

Runtime telemetry and CLI tracing

The runtime collects lightweight metrics on every run (RunResult.metrics):

Enable verbose trace logs:

spanda run robot.sd --trace-scheduler --trace-tasks
spanda sim robot.sd --replay --trace-scheduler

Trace logs are prefixed with trace-scheduler:, trace-task:, or trace-replay: in the runtime log stream.

See examples/concurrency.sd for a runnable program combining task priorities, spawn handles, parallel aggregation, and channels.

Runtime budget enforcement

Per-task budget { } blocks are validated at compile/verify time and enforced at runtime:

Agent mailboxes

Declare typed agent channels in the robot body:

agent Vision { goal "see"; plan { send_agent("Planner", scan); } }
agent Planner { goal "plan"; plan { let msg = recv_agent(); } }
Vision -> Planner: Scan;

send_agent and recv_agent require active agent context (inside agent.plan()).

Fleet peer messaging

Peer robots declared with robot RoverA; can exchange messages via namespaced topics:

subscribe RoverA.pose;
peer_send("RoverA", "pose", current_pose);
receive RoverA.pose to pose_msg;

Static concurrency lint

spanda lint warns on suspicious channel flows:

spanda lint robot.sd
spanda lint --json robot.sd

Fleet CLI

spanda fleet run prints deploy targets and peer-robot wiring from a program, then runs the in-process simulation (same engine as spanda run / spanda sim):

spanda fleet run examples/communication/multi_robot_fleet.sd
spanda fleet run --trace-scheduler --trace-tasks fleet.sd
spanda fleet run --persist-telemetry fleet.sd

Output includes:

This is a single-process fleet simulation today; true multi-process orchestration (one subprocess per deploy target) is planned separately.

For fleet recovery and mission continuity (takeover, delegation, succession) across deployed agents, see fleet-distributed.md, self-healing.md, and mission-continuity.md. CLI: spanda demo self-healing, spanda demo continuity.

TypeScript interpreter parity

The npm spanda package mirrors the Rust concurrency surface in the TypeScript interpreter:

Use compile(source) + run(program, { backend }) from src/compile.ts, or the CLI with the default TS backend. Rust-native execution (rust-cli / rust-native backends) uses the same semantics via spanda-core.

Tests: tests/concurrency.test.ts, tests/concurrency-extended.test.ts (TS); crates/spanda-core/tests/concurrency_extended.rs (Rust).