Phase 1 — semantics doc
docs/002. hsmc-semantics-formal.md
Phase 2 — deterministic execution journal
hsmc/src/journal.rs (TraceEvent, JournalSink, ActionKind)
hsmc-macros: __chart_journal! emission at every observable site
(entry/exit/action/transition/timer arm-cancel-fire/during/emit/
event delivery/terminate). Per-state during count helpers, stable
chart-hash via FNV-1a. Action observation moved out of run_action
and into call sites so ActionKind is accurate.
Public API on the generated machine: journal(), take_journal(),
clear_journal(), CHART_HASH.
Phase 3 — verification crate (Creusot scaffolding)
verification/{Cargo.toml,mise.toml,requirements.txt,INVARIANTS.md}
verification/src/{event_queue.rs,timer_table.rs} — Pearlite
contracts on simplified mirrors of the runtime types. cargo
creusot generates Coma IR cleanly; VCs not yet discharged.
Correspondence tests drive the verified mirror and the runtime
type with the same op sequences and assert observable parity.
Tests — 113 deterministic-flow tests across 22 files. Each compares a
hand-built expected journal byte-for-byte against actual, anchored
to a spec section: initial entry, lateral/self/up transitions,
bubbling, timers, emit, ordering, durings, step/dispatch
equivalence, root-targeting, current_state, termination.
Tooling — mise.toml (root): stable Rust + cargo subcommands.
verification/mise.toml: pinned nightly-2025-11-13 + opam + python
venv + uv. just verify is fully self-installing.
Bug fix — root state targetable by the chart's own name (e.g.
`on(Trig) => ChartName;`) per spec §"How transitions work".
CHART_HASH — stable FNV-1a fingerprint over the chart's structural
definition; mismatch on replay = different chart.
67 lines
2.5 KiB
TOML
67 lines
2.5 KiB
TOML
[package]
|
|
name = "hsmc"
|
|
version.workspace = true
|
|
edition.workspace = true
|
|
license.workspace = true
|
|
authors.workspace = true
|
|
repository.workspace = true
|
|
homepage.workspace = true
|
|
description = "Hierarchical state machines (statecharts) with a declarative proc macro."
|
|
readme = "../README.md"
|
|
keywords = ["statechart", "state-machine", "hsm", "embassy", "embedded"]
|
|
categories = ["embedded", "no-std", "asynchronous", "rust-patterns"]
|
|
autoexamples = false
|
|
exclude = ["examples/microwave_tui.rs"]
|
|
|
|
[features]
|
|
default = []
|
|
tokio = ["dep:tokio"]
|
|
embassy = ["dep:embassy-sync", "dep:embassy-time", "dep:embassy-futures"]
|
|
# Auto-tracing backends — pick at most one. When the user opts a chart
|
|
# into `trace;`, the macro emits calls to an internal dispatcher that
|
|
# routes to whichever backend is enabled here. With NO backend feature
|
|
# enabled, `trace;` is a no-op (zero overhead). Enabling more than one
|
|
# backend produces a duplicate-macro compile error — that's intentional.
|
|
trace-defmt = ["dep:defmt"]
|
|
trace-log = ["dep:log"]
|
|
trace-tracing = ["dep:tracing"]
|
|
# Journaling backend. When enabled, the macro emits calls into an
|
|
# in-memory journal recording every observable atom: entries, exits,
|
|
# action invocations, during start/cancel, timer arm/cancel/fire, emits,
|
|
# event delivery, transitions, and termination. Used to make charts
|
|
# replay-deterministic and to catch semantic regressions in CI.
|
|
# Requires `alloc` (the journal grows dynamically). Off by default —
|
|
# zero overhead and no `alloc` requirement when not enabled.
|
|
journal = []
|
|
|
|
[dependencies]
|
|
hsmc-macros = { path = "../hsmc-macros", version = "0.2.0" }
|
|
heapless = "0.8"
|
|
tokio = { version = "1", features = ["sync", "time", "rt", "macros"], optional = true }
|
|
embassy-sync = { version = "0.8", optional = true }
|
|
embassy-time = { version = "0.5", optional = true }
|
|
embassy-futures = { version = "0.1", optional = true }
|
|
defmt = { version = "0.3", optional = true }
|
|
log = { version = "0.4", optional = true }
|
|
tracing = { version = "0.1", default-features = false, optional = true }
|
|
|
|
[target.'cfg(any(target_os = "linux", target_os = "macos", target_os = "windows"))'.dev-dependencies]
|
|
trybuild = "1"
|
|
tokio = { version = "1", features = ["rt-multi-thread", "macros", "sync", "time", "test-util"] }
|
|
ratatui = "0.28"
|
|
crossterm = { version = "0.28", features = ["event-stream"] }
|
|
futures = "0.3"
|
|
|
|
[[example]]
|
|
name = "microwave"
|
|
required-features = ["tokio"]
|
|
|
|
[[example]]
|
|
name = "during_radio"
|
|
required-features = ["tokio"]
|
|
|
|
[[example]]
|
|
name = "embassy_full"
|
|
required-features = ["embassy"]
|
|
crate-type = ["lib"]
|