phoxal 0.12.0

Phoxal — production-oriented autonomous robot framework (engine, model, typed bus, contracts).
Documentation
# phoxal

The Phoxal robot framework as one crate: a manifest-driven stack for
production-oriented autonomous robots — a typed Zenoh bus, a derive-based runtime
authoring model, and a single, dated API-version contract set.

> **Greenfield rewrite in progress** (`tmp/framework-rewrite`). The bus + runtime
> engine and the macros are new; the official runtime/simulator/tool crates are
> being re-ported onto this surface and are temporarily out of the workspace.

```toml
[dependencies]
phoxal = "0.11"
```

## The authoring model

A runtime is **one struct of typed handles + one annotated inherent impl**. The
struct declares the contracts it uses (as handle fields) and the one API version
it runs against; the impl declares the lifecycle.

```rust,ignore
use phoxal::api::y2026_1 as api;
use phoxal::prelude::*;

#[derive(phoxal::Runtime)]
#[phoxal(id = "avoid-obstacles", api = y2026_1)]
struct AvoidObstacles {
    state:  Latest<api::drive::State>,
    target: Publisher<api::drive::Target>,
}

#[phoxal::runtime]
impl AvoidObstacles {
    #[setup]
    async fn setup(ctx: &mut SetupContext<Self>) -> Result<Self> {
        Ok(Self {
            state:  ctx.subscribe(api::topic::new().drive().state()).latest().await?,
            target: ctx.publisher(api::topic::new().drive().target()).await?,
        })
    }

    #[step(hz = 50)]
    async fn step(&mut self, step: StepContext) -> Result<()> {
        let now = step.time();
        // read inputs, publish version-local bodies
        self.target.publish_at(now, api::drive::Target {
            linear_x_mps: 0.2, angular_z_radps: 0.0,
        }).await?;
        Ok(())
    }
}

fn main() -> phoxal::Result<()> { phoxal::run::<AvoidObstacles>() }
```

Key rules the example shows:

- Import exactly one dated API module (`use phoxal::api::y2026_1 as api;`) and
  declare it on the derive (`#[phoxal(api = y2026_1)]`, **mandatory**).
- Handle fields use version-local bodies: `Publisher<api::drive::State>`,
  `Latest<api::drive::Target>`. The derive emits a `ContractBody<Api = R::Api>`
  assertion, so a body from another API version is a **compile error**.
- Topics are api-local: `api::topic::new().drive().state()`.
- The wire body is the plain payload; `api_version`, family, and codec ride bus
  metadata. Normal runtimes never open Zenoh — the runner opens the
  bundle-selected bus profile before `#[setup]`.
- `cargo run --example runtime_control_loop emit-apis` prints the runtime's
  static metadata as one JSON document (the `emit-apis` subcommand) and exits.

## Modules

| Module | What it is |
|---|---|
| [`phoxal::api`] | Dated API-version modules (`y2026_1`, …) generated by `phoxal_api_tree!`: the marker `enum Api` (`ApiVersion`), version-local wire bodies + their `ContractBody` impls, and the api-local `topic` builders. |
| [`phoxal::bus`] | The Zenoh-native `bus_abi` boundary: the key scheme `<namespace>/robots/<robot-id>/<topic>`, the MessagePack codec, the `BusMetadata` attachment, and the body-typed handles `Publisher`/`Subscriber`/`Latest`. |
| [`phoxal::runtime`] | The static metadata traits the macros target, `SetupContext`/`StepContext`, the clock (`RealClock`/`TestClock`) + scheduler, `ParticipantLaunch`, `emit-apis`, and the runner (`run` / `tokio::run`). |
| [`phoxal::model`] | Authored manifest schemas (`robot.yaml`, `structure.urdf`, …). |

## Entrypoints

- Default (blocking): `fn main() -> phoxal::Result<()> { phoxal::run::<R>() }`.
- Advanced (async): `phoxal::tokio::run::<R>().await` for custom Tokio mains.

## Status

Pre-1.0, building in public. One API version per robot graph (`#[phoxal(api =
y2026_1)]`); the wire body is the plain payload and the version identity rides bus
metadata, never the key. Compatibility is checked at build/check time
(`emit-apis` + `phoxal-cli check`), never by introspecting a running binary.

## License

AGPL-3.0-only. A commercial license is available — see the repository.