ready-active-safe 0.1.3

Lifecycle engine for externally driven systems
Documentation
# Decision

The outcome of processing an event through [`Machine::on_event`](machine.md).

A `Decision` captures two orthogonal concerns:
- Whether the system should change mode ([`ModeChange`]mode_change.md)
- What commands the runtime should execute

Decisions are **pure data** — they describe intent, not side effects.

## Constructors

```rust
use ready_active_safe::Decision;

// Stay in the current mode
let stay: Decision<&str, &str> = Decision::stay();
assert!(stay.is_stay());

// Transition to a new mode
let go: Decision<&str, &str> = Decision::transition("active");
assert!(go.is_transition());
assert_eq!(go.target_mode(), Some(&"active"));
```

## Chaining Commands — `emit` / `emit_all`

```rust
use ready_active_safe::Decision;

let d: Decision<&str, &str> = Decision::transition("active")
    .emit("initialize")
    .emit("begin");
assert_eq!(d.commands(), &["initialize", "begin"]);
```

```rust
use ready_active_safe::Decision;

let d: Decision<&str, &str> = Decision::stay()
    .emit_all(["log", "notify"]);
assert_eq!(d.commands(), &["log", "notify"]);
```

## Accessors

```rust
use ready_active_safe::Decision;

let d: Decision<&str, &str> = Decision::transition("active").emit("go");

// mode_change returns the full ModeChange
assert_eq!(d.mode_change().map(|mc| mc.target), Some("active"));

// target_mode is a shortcut
assert_eq!(d.target_mode(), Some(&"active"));

// commands
assert_eq!(d.commands(), &["go"]);
```

## Consuming Accessors — `into_parts`, `into_mode_change`, `into_commands`

```rust
use ready_active_safe::Decision;

let d: Decision<&str, &str> = Decision::transition("active")
    .emit("initialize");

let (change, commands) = d.into_parts();
assert_eq!(change.map(|mc| mc.target), Some("active"));
assert_eq!(commands, vec!["initialize"]);
```

```rust
use ready_active_safe::Decision;

let d: Decision<&str, &str> = Decision::transition("safe");
let change = d.into_mode_change();
assert_eq!(change.map(|mc| mc.target), Some("safe"));
```

```rust
use ready_active_safe::Decision;

let d: Decision<&str, &str> = Decision::stay().emit("log").emit("ack");
let commands = d.into_commands();
assert_eq!(commands, vec!["log", "ack"]);
```

## `apply` — consume decision and update mode

```rust
use ready_active_safe::prelude::*;

let d: Decision<&str, &str> = transition("active").emit("go");
let (mode, commands) = d.apply("ready");
assert_eq!(mode, "active");
assert_eq!(commands, vec!["go"]);
```

## `apply_checked` — apply with policy guard

```rust
use ready_active_safe::prelude::*;

let d: Decision<&str, &str> = transition("active").emit("go");
let (mode, commands) = d.apply_checked("ready", &AllowAll)?;
assert_eq!(mode, "active");

let d2: Decision<&str, &str> = transition("active");
let err = d2.apply_checked("ready", &DenyAll).unwrap_err();
assert!(matches!(err, LifecycleError::TransitionDenied { .. }));
# Ok::<(), LifecycleError<&str>>(())
```

## Edge Cases

A stay decision with `apply` returns the current mode unchanged:

```rust
use ready_active_safe::prelude::*;

let d: Decision<&str, ()> = stay();
let (mode, commands) = d.apply("ready");
assert_eq!(mode, "ready");
assert!(commands.is_empty());
```

A stay decision with `apply_checked` always succeeds (no transition to check):

```rust
use ready_active_safe::prelude::*;

let d: Decision<&str, ()> = stay();
let (mode, _) = d.apply_checked("ready", &DenyAll)?;
assert_eq!(mode, "ready");
# Ok::<(), LifecycleError<&str>>(())
```

## See Also

- [`ModeChange`]mode_change.md — the transition payload inside a `Decision`
- [Free functions]functions.md`stay()`, `transition()`, `ignore()` convenience wrappers
- [`Machine`]machine.md — the trait that produces `Decision` values