After Opt #2 added six bounded `get_unchecked` blocks per chart, the
project's two "no unsafe" claims (README §"At a glance" + spec §11
rule 12) were stale, and there was no single place answering "how
do I know the unsafe is correct?".
This commit closes both gaps:
- New: docs/004. unsafe-safety-contract.md
Inventories every unsafe site, names the predicate, names what
establishes it (codegen-time guarantee), and lists the five
layered checks (const-eval, debug_assert, miri, mutants, Creusot)
with current status. Documents what's *not* yet covered (the
16-of-53 missed mutants in the LCA depth walk are behavior, not
safety; transition() codegen invariants are still Phase-4 for
Creusot). Includes a maintenance checklist for future unsafe.
- README points at the doc and gives an honest one-paragraph
summary instead of "no unsafe".
- Spec rule 12 rewritten from "no unsafe in generated code" to
"bounded unsafe in generated code, none in user code" with a
pointer to the audit.
- justfile gains `just safety` — runs const-eval (cargo check),
debug_assert tests, miri, and mutants in one shot. Mirrors the
doc's audit matrix so the doc never goes stale silently.
A transition whose target is already active (an ancestor of the
innermost active state) now unwinds only the subtree strictly below
the target. The target's entry/exit actions do not fire and its
default() does not re-descend — you cannot re-enter a state you never
left. The old behavior could loop infinitely when a child transitioned
to its own parent.
Self-transitions (target == innermost) and lateral transitions are
unchanged. See spec §2.6 and tests §T2.8 / T2.8b / T2.8c.
Also in this release:
* Full workspace justfile (check, test, coverage, mutants, pre-release)
* Unit tests covering all __private internals; `cargo mutants` now exits
clean (38 caught, 178 unviable, 0 missed, 0 timeouts).
* .config/nextest.toml with slow-timeout/terminate-after so hung tests
fail fast rather than wedging cargo-mutants runs.
BREAKING CHANGE: transition-to-ancestor semantics. Machines that relied
on the old re-entry behavior of up-transitions will observe different
exit/entry action sequences.
Hierarchical statechart codegen for Rust with:
- during: activities for state-scoped concurrent I/O
- field-list borrowing (split-borrow inside the run loop)
- embassy-first async with tokio support
- STATE_CHART const for tooling/inspection
- examples: microwave, microwave_tui, embassy_full, during_radio