Concepts
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::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
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 (csm/mod.rs). 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
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
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
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
- Example: sensor monitoring with CSM (source:
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.