Skip to main content

turnframe_test/explore/
mod.rs

1//! Bounded workflow exploration (spec §8.5).
2//!
3//! Fixtures prove that the states you thought of behave. Exploration proves
4//! something else: that *every state reachable from them* still satisfies the
5//! Flow Map invariants. You give the explorer a [`WorkflowModel`] — the initial
6//! states, the commands worth trying and a pure simulation of each — and it
7//! walks the reachable states breadth-first, projecting each one and checking
8//! the §8.4 rules on it.
9//!
10//! Because the search is breadth-first and states are deduplicated by their
11//! canonical JSON, a violation is reported with the *shortest* command path
12//! that reaches it, which is usually the smallest reproduction you can get.
13//!
14//! One of the rules is a position rather than a mechanical check: a case's
15//! identity outlives its content, so an absent state means *not yet* and never
16//! *no longer*. A projector that gives an absent state a terminal phase or an
17//! outcome, or a model that answers a command by dropping the case, is
18//! describing a case that ends by disappearing, and the explorer reports both.
19//! See [`ExplorationViolationKind::CaseEndsByDisappearing`].
20//!
21//! ```
22//! use turnframe_test::explore::{ExplorationLimits, explore};
23//! use turnframe_test::workflows::trip::{TripModel, TripWorkflow};
24//!
25//! let report = explore(
26//!     &TripWorkflow::default(),
27//!     &TripModel::default(),
28//!     ExplorationLimits::smoke(),
29//! );
30//! assert!(report.is_clean(), "{}", report.describe());
31//! assert!(report.states_explored > 1);
32//! ```
33
34mod limits;
35mod model;
36mod report;
37mod search;
38
39pub use limits::ExplorationLimits;
40pub use model::{SimulatedTransition, WorkflowModel};
41pub use report::{ExplorationReport, ExplorationViolation, ExplorationViolationKind};
42pub use search::{EXPLORATION_CASE_ID, EXPLORATION_REVISIONS, explore, reachable_states};