Causal State Machine
The Causal State Machine (CSM) is the connector between a Causaloid’s verdict and an effect on the outside world. It lives in deep_causality/src/types/csm_types and is built around two ideas: a state is “active” when its Causaloid evaluates to an active effect; an action is a function that runs when its paired state is active.
What it is
Section titled “What it is”A CSM holds a thread-safe map of (CausalState, CausalAction) pairs:
pub struct CSM<I, O, C>where I: Default + Clone, O: CsmEvaluable + Default + Debug + Clone, C: Clone,{ state_actions: Arc<RwLock<CSMMap<I, O, C>>>,}The constructor takes a slice of pairs. The map sits behind Arc<RwLock<...>>, so the CSM is shareable across threads and can grow or shrink at runtime through add_state, remove_state, and update_state, each implemented in its own submodule.
States and actions
Section titled “States and actions”A CausalState<I, O, C> is small. It carries:
- an integer
id; - a
versionfor tracking iterations of the same logical state; - a
PropagatingEffect<I>of pre-bound data; - the
Causaloidthat decides whether this state is active; - an optional
UncertainParameterfor states whose Causaloid emits an uncertain effect.
A CausalAction is smaller still:
pub struct CausalAction { action: fn() -> Result<(), ActionError>, description: &'static str, version: usize,}action.fire() invokes the function pointer. The action surface is kept minimal on purpose: the conditional logic lives in the Causaloid, not in the action.
Evaluation
Section titled “Evaluation”Two entry points are exposed:
eval_single_state(id, data)runs the Causaloid for one state against caller-supplied data.eval_all_states()walks every registered state and runs its Causaloid against the data already bound to the state.
In both cases the effect returned by the Causaloid is inspected:
let is_active = match &effect.value { EffectValue::Value(val) => val .is_active(state.uncertain_parameter().as_ref()) .map_err(CsmError::Causal)?, // Other variants (RelayTo, Error, etc.) are inactive for action firing. _ => false,};
if is_active { self.fire_action_with_ethos_check(state, action, effect)?;}is_active is delegated to the CsmEvaluable trait. A deterministic effect resolves to true or false directly; an uncertain effect runs its hypothesis test against the state’s UncertainParameter. Anything that is not a value (relay, error, none) is treated as inactive and fires nothing.
When to reach for it
Section titled “When to reach for it”A CSM fits whenever the question “should this fire?” has to follow a causal verdict rather than a fixed threshold. Common shapes:
- sensor monitoring with thresholds that depend on context, not on the latest reading alone;
- alert routing where the alert depends on a pattern across signals;
- control loops in which an action should run only when an uncertain condition clears a confidence bar.
The CSM does not own a scheduler. The host application decides when to call eval_*. That keeps it cheap to embed in async runtimes, batch jobs, or hard real-time loops.
See also
Section titled “See also”- Example: sensor monitoring with CSM, backed by
examples/csm_examples/csm_basic. - Concept: Causaloid, the unit that supplies the verdict each state acts on.
- Concept: Effect Ethos, for policy checks that should gate an action before it fires.
- Concept: Uncertainty, for the hypothesis tests applied when a state’s Causaloid emits an uncertain effect.