ready-active-safe 0.1.3

Lifecycle engine for externally driven systems
Documentation
# Vision

`ready-active-safe` is a **lifecycle engine** for **externally driven systems**.

It is built for systems whose “state changes” are actually *operational phases* (modes) with
clear contracts: start up, operate, fail safely, and recover. These are driven by events from the outside
world (hardware, runtimes, supervisors, operators).

This document defines the product identity: what the crate is, who it is for, what it will and
will not do, and how we keep the implementation aligned as the API grows.

---

## Problem Statement

Many real systems are not well modeled as “anything can transition to anything” state graphs.
They have a lifecycle:

- Become ready (initialize and validate prerequisites)
- Become active (perform primary work)
- Become safe (shut down, contain faults, recover)

Yet implementation often devolves into ad-hoc flags and scattered conditionals, or into state
machines that interleave effects with transitions, making correctness and auditing hard.

`ready-active-safe` provides a small, explicit vocabulary and a pure decision boundary:
given a current mode and an external event, return **data** describing the next mode (optional)
and commands to execute.

---

## Core Thesis

**Separate decision logic from execution.**

- The **machine** is pure: `(mode, event) -> Decision`.
- The **runtime** (external) owns the current mode, feeds events, checks policy, and executes commands.
- The **policy** (external) determines which transitions are allowed.
- **Recovery** is modeled, not bolted-on: Safe and Safe→Ready are first-class lifecycle paths.

---

## Target Users

This crate is for developers who need:

- Deterministic, testable lifecycle logic without I/O or runtime coupling
- Clear operational phases (modes) with safe/fail/recovery semantics
- Auditability and replay of lifecycle decisions (by logging or via the `journal` feature)

Representative domains:

- Embedded controllers and robotics
- XR/graphics session lifecycles (e.g., OpenXR session state changes)
- Services with explicit readiness/active/safe shutdown boundaries
- Lab/industrial instrumentation and supervisory control

---

## Non-Goals (Identity Guards)

These are deliberate “no’s” that prevent vision drift:

- **Not a general-purpose FSM library** (arbitrary graphs, hierarchical substates, statecharts)
- **No inline side effects** in transition logic (effects belong in runtimes/executors)
- **No required proc-macro DSL** in the core crate (plain Rust traits/structs remain the default)
- **No prescribed runtime** (sync/async/executor choice stays with the user)
- **Not a workflow engine / orchestrator** (no event queues, schedulers, or long-running job graphs)
- **No mandated serialization format** (adapters may offer `serde`, but the core stays lean)

If a feature makes the crate “more like a generic FSM framework”, it is likely out of scope.

---

## The Contract (What Must Always Stay True)

The following are invariants that define correctness and usability:

1. **Purity boundary**: `Machine::on_event` is deterministic and performs no I/O.
2. **Decisions are data**: a `Decision` describes intent. The runtime performs effects.
3. **External control**: the system never transitions itself. Events come from outside.
4. **Recovery-first**: Safe/recovery is modeled as a normal lifecycle phase, not exceptional control flow.
5. **Portability**: core types remain `no_std`-compatible (requires `alloc`).

---

## Product Shape (How It “Feels” To Use)

The intended user flow:

1. Define `Mode`, `Event`, and `Command` types.
2. Implement `Machine` with a `match (mode, event)`.
3. In a runtime loop, feed events and execute commands.
4. Add a `Policy` when you need to restrict transitions.
5. Add logging/journaling when you need audit/replay.

Ergonomics goal: the first useful lifecycle should take **minutes**, not hours.

### Rails-Like DX (In Rust)

“Rails-like” here does not mean “framework”.
It means the crate has a clear, pleasant default path:

- `use ready_active_safe::prelude::*;` is the one obvious import
- examples are copy/paste friendly and kept honest with doctests
- default features are “batteries included”, but never ship stubs
- the escape hatches are real: you can always drop down to `machine.decide(...)`

---

## What “Production Ready” Means Here

“Production ready” is not “feature complete”. It means:

- The **core contract** is stable and well-documented.
- Semantics are explicit (especially policy denial, command ordering, and replay expectations).
- There are no hidden side effects, no `unsafe`, and no panic-based control flow in library code.
- There is a clear path to integrating with real systems (sync loops today, with optional runtime and journaling later).

The crate can be production-ready as a **pure lifecycle kernel** even if optional
`runtime`/`time`/`journal` facilities evolve over time.