# Reliability
How `ready-active-safe` earns and maintains your trust.
---
## Trust Foundations
### Doc Tests as Executable Specification
We treat examples as part of the API surface.
- Rustdoc doctests run in CI for the crate API documentation.
- We also run `rustdoc --test` against key markdown docs (`README.md`,
`USER_GUIDE.md`, `RELIABILITY.md`, `COOKBOOK.md`).
The examples in the documentation are not aspirational. If the API changes, the
examples should break in CI.
### No Unsafe
`unsafe` is forbidden at the crate level via `#[forbid(unsafe_code)]` in `Cargo.toml` lint configuration. This is not a guideline. It is a compiler-enforced guarantee. There are no `unsafe` blocks in the crate, and contributions that add `unsafe` will not compile.
### No Panics in Library Code
The following are denied by Clippy configuration in `Cargo.toml`:
| `unwrap()` | `clippy::unwrap_used` |
| `expect()` | `clippy::expect_used` |
| `panic!()` | `clippy::panic` |
| `todo!()` | `clippy::todo` |
| `unimplemented!()` | `clippy::unimplemented` |
| `dbg!()` | `clippy::dbg_macro` |
| `println!()` | `clippy::print_stdout` |
| `eprintln!()` | `clippy::print_stderr` |
These are not warnings. They are hard errors. Code that calls `unwrap()` in library code will not compile.
### Pure Machine Trait
`Machine::on_event` takes `&self`. The trait contract guarantees:
- No mutation of the machine.
- No I/O.
- No access to global state.
- Deterministic output for a given `(&self, &mode, &event)` input.
This purity means you can test lifecycle logic with plain assertions. No test harness, no mocking framework, no setup/teardown.
### MSRV 1.75
The minimum supported Rust version is 1.75, declared in `Cargo.toml` via `rust-version = "1.75"`. CI builds against this version on every PR. MSRV bumps are treated as minor version changes under SemVer.
### No Runtime Dependencies
The library has no external runtime dependencies. The published crate depends only on `core` and `alloc` from the Rust standard library.
Development and tests may use `dev-dependencies` (for example, the `behave` testing DSL). These do not affect downstream users of the library.
### Journal for Auditability
The `journal` feature records every mode transition, enabling post-hoc analysis of
system behavior. This is designed for systems where you need to answer
"what happened and why" after the fact.
---
## Current Commitments
These are guarantees the crate makes at v0.1.0 and intends to maintain.
### no_std Core
The core types (`Machine`, `Decision`, `ModeChange`, `Policy`, `LifecycleError`) are available in `no_std` environments with `alloc`. This is tested in CI via `cargo test --no-default-features`. Enabling features never breaks `no_std` compatibility for the core types.
### Additive Features
Feature flags are strictly additive. Enabling a feature adds functionality. It never removes or alters existing behavior. A program that compiles with `--no-default-features` will also compile with `--all-features`.
### #[non_exhaustive] on Public Types
All public structs and enums are marked `#[non_exhaustive]`. This allows the crate to add fields, variants, and associated data in minor versions without breaking downstream code. Users must include wildcard arms in match expressions, which is the intended trade-off.
### SemVer Compliance
The crate follows Semantic Versioning strictly:
- **Patch** (0.1.x): Bug fixes, documentation improvements, no API changes.
- **Minor** (0.x.0): New features, new types, new trait methods with defaults, MSRV bumps.
- **Major** (x.0.0): Breaking API changes, removed types, changed trait signatures.
The `VERSION` file is the single source of truth. CI enforces that `Cargo.toml` matches it.
---
## Stated Limitations
Honesty about what the crate does not do is part of reliability.
### 0.1.0 API Evolution
The crate is at version 0.1.0. Under SemVer, the 0.x series permits breaking changes in minor releases. The crate will stabilize its API based on real-world usage before committing to 1.0. Pre-1.0 users should pin to a specific minor version:
```toml
ready-active-safe = "=0.1.0"
```
### In-Memory Journal Only
The `journal` feature stores transition records in memory. There is no built-in
persistence, no file I/O, and no database integration. Persistent storage is an
adapter concern that users implement on top of the journal's data types.
### No Serialization
Types do not implement `serde::Serialize` or `serde::Deserialize`. Serialization support will be offered as an optional feature flag, not baked into the core types. Until then, users who need serialization should implement it for their own mode, event, and command types.
### No Async
The `Machine` trait is synchronous. `on_event` returns a `Decision` directly, not a `Future`. Async lifecycle processing is a runtime concern: an async runtime can call the synchronous `Machine::on_event` inside an async task. A dedicated async adapter may be offered in the future as a separate feature or crate.
### No Built-In Timer
The `time` feature provides clock and deadline types. It does not provide a
timer runtime. Timer scheduling, tick generation, and deadline monitoring are
the responsibility of the external runtime, not the lifecycle engine.
---
## Evaluation Path for New Users
If you are evaluating `ready-active-safe` for your project, here is a suggested approach:
### 1. Read the Quick Start
The example in `src/lib.rs` (also shown in `USER_GUIDE.md`) demonstrates the entire API surface in under 30 lines. If the pattern does not fit your problem, you will know immediately.
### 2. Implement a Toy Lifecycle
Define three modes, a few events, and implement `Machine`. This should take under 15 minutes. If it feels natural, the crate is a good fit. If you are fighting the API, it may not be.
### 3. Write Tests
Test your machine with plain assertions. No setup, no mocking. If the tests are easy to write and easy to read, the architecture is working.
### 4. Check the Constraints
- Do you need `no_std`? The core types work.
- Do you need time-aware transitions? Use the `time` feature types and/or encode time in your event type.
- Do you need an event loop? Use `runtime::Runner` as a boring reference loop, or write your own loop.
- Do you need transition history? Use `journal::InMemoryJournal` (or log decisions yourself).
### 5. Read the Source
The crate is intentionally small and readable. `model.rs` defines all core types.
`error.rs` defines the error type. `time.rs`, `runtime.rs`, and `journal.rs` are
reference-quality feature modules.
There are no proc macros, no code generation, and no hidden complexity. What you
see is what you get.