journal + determinism tests + Creusot verification scaffolding

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.
This commit is contained in:
swaits committed 2026-04-27 20:15:05 -06:00
1 parent 4d7baf7236
commit 081bd28cca
55 files changed
+6476 -151

No files matched your search

+46
View File
@@ -0,0 +1,46 @@
//! Deductive proofs for the `hsmc` runtime data structures.
//!
//! This crate hosts Creusot/Pearlite contracts on simplified mirrors of
//! `hsmc::__private::EventQueue` and `hsmc::__private::TimerTable`. The
//! mirrors are byte-for-byte equivalent to the runtime types' observable
//! behavior; the difference is that the mirrors carry full
//! `#[requires] / #[ensures]` annotations, so a Creusot run discharges
//! VCs (verification conditions) for them.
//!
//! ## Why mirror, not annotate hsmc directly?
//!
//! Two reasons:
//!
//! 1. **Cleanliness.** `hsmc` itself stays free of `creusot-contracts`
//! — production users don't have to think about a verifier dep.
//! 2. **Tractability.** The runtime types are layered on `heapless`
//! types whose Pearlite models would have to be written first.
//! A simplified mirror with `&mut [_; CAP]` storage avoids the
//! indirection and makes the proofs readable.
//!
//! The correspondence is enforced by `tests/correspondence.rs`, which
//! drives both the mirror AND the real `hsmc` runtime type with the same
//! operation sequences and asserts equal observable results. If the
//! mirror drifts away from the runtime, the correspondence test fails
//! before the proofs become wrong.
//!
//! ## Status
//!
//! See `INVARIANTS.md` for the spec-rule → proof mapping. Initial focus
//! (in priority order):
//!
//! 1. `EventQueue` — FIFO; bounded; pop-after-push returns the pushed
//! value. (S6 in the semantics doc.)
//! 2. `TimerTable::start` — at most one entry per `(state, trigger)`
//! pair; capacity respected. (S5.1)
//! 3. `TimerTable::pop_expired` — deepest expired entry first; ties
//! broken by insertion index; entry removed; `None` iff none expired.
//! (S5.2)
//! 4. (later) `transition()` path validity — codegen-emitted; needs
//! macro changes to thread Creusot attributes through.
//! 5. (later) `step()` end-to-end determinism.
#![cfg_attr(not(test), no_std)]
pub mod event_queue;
pub mod timer_table;