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.
2.8 KiB
2.8 KiB
Changelog
All notable changes to this workspace are documented here. The format follows Keep a Changelog and this project adheres to Semantic Versioning.
0.2.0 — 2026-04-22
Fixed — behavior-visible (breaking)
- Up-transitions no longer exit and re-enter the target ancestor.
A transition whose target is already active (i.e., the target is on
the path from root to the innermost active state, and the target is
not itself the innermost) now unwinds only the subtree strictly
below the target. The target's
exit:/entry:actions do NOT fire, and itsdefault(...)does NOT re-descend. You cannot re-enter a state you never left. The old semantics (exit target, re-enter, re-descend to default) was incorrect and could produce infinite re-entry when a child transitioned to its own parent. See spec §2.6, §T2.8, §T2.8b, §T2.8c. - Self-transitions (
target == innermost) are unchanged: the target is still exited and re-entered, timers still restart. - Lateral transitions (target not on the active path) are unchanged.
current_state()may now return a composite state when it became innermost via an up-transition.
0.1.0 — 2026-04-22
Initial release.
Added
statechart!proc macro: declarative Harel-style hierarchical state machines with nested states,entry:/exit:actions, LCA-aware transitions, default-child descent, andterminatesemantics.- Typed event payload bindings:
on(PacketRx { rssi: i16 }) => handler;expands into a typed handler signature, verified at compile time. - Timer triggers:
on(after Duration::from_secs(5)) => Next;andon(every Duration::from_millis(100)) => tick;, tracked in a fixed-capacityTimerTable. during:activities (v0.2 semantics): async functions that run while a state is on the active path and produce events. Multiple concurrent durings on a single state borrow disjoint&mutslices of the context, enforced by Rust's native split-borrow.- Runtime:
machine.run().awaitraces active durings, the external event channel, and timer deadlines — one future, cooperative on embassy, multi-threaded on tokio. - Feature gates:
tokio(std + tokio runtime),embassy(no_std + embassy-time / embassy-sync). The two features are mutually exclusive and enforced viacompile_error!. no_stdby default;stdpulled in only via thetokiofeature.STATE_CHARTconst anddispatch()helper for introspection.- Workspace:
hsmc(library) andhsmc-macros(proc-macro front-end) both at0.1.0.
Docs
- 320-line
README.mdwith quickstart, semantics, and examples. - Runnable examples:
microwaveandduring_radiounder tokio,embassy_fullunder embassy. - Behavior, hierarchy, during, timer, and payload-binding test suites (13 integration tests).