franken-decision-0.2.0 has been yanked.
Decision Contract schema and runtime for FrankenSuite (bd-3ai21).
The third leg of the foundation tripod alongside franken_kernel (types)
and franken_evidence (audit ledger). Every FrankenSuite project that
makes runtime decisions uses this crate's contract schema.
Core abstractions
- [
DecisionContract] — trait defining state space, actions, losses, and
posterior updates. Implementable in <50 lines.
- [
LossMatrix] — non-negative loss values indexed by (state, action),
serializable to TOML for runtime reconfiguration.
- [
Posterior] — discrete probability distribution with O(|S|)
no-allocation Bayesian updates.
- [
FallbackPolicy] — calibration drift, e-process breach, and
confidence interval width thresholds.
- [
DecisionAuditEntry] — links decisions to [EvidenceLedger] entries.
Example
use franken_decision::{
DecisionContract, EvalContext, FallbackPolicy, LossMatrix, Posterior, evaluate,
};
use franken_kernel::DecisionId;
struct MyContract {
states: Vec<String>,
actions: Vec<String>,
losses: LossMatrix,
policy: FallbackPolicy,
}
impl DecisionContract for MyContract {
fn name(&self) -> &str { "example" }
fn state_space(&self) -> &[String] { &self.states }
fn action_set(&self) -> &[String] { &self.actions }
fn loss_matrix(&self) -> &LossMatrix { &self.losses }
fn update_posterior(&self, posterior: &mut Posterior, observation: usize) {
let likelihoods = [0.9, 0.1];
posterior.bayesian_update(&likelihoods);
}
fn choose_action(&self, posterior: &Posterior) -> usize {
self.losses.bayes_action(posterior)
}
fn fallback_action(&self) -> usize { 0 }
fn fallback_policy(&self) -> &FallbackPolicy { &self.policy }
}
let contract = MyContract {
states: vec!["good".into(), "bad".into()],
actions: vec!["continue".into(), "stop".into()],
losses: LossMatrix::new(
vec!["good".into(), "bad".into()],
vec!["continue".into(), "stop".into()],
vec![0.0, 0.3, 0.8, 0.1],
).unwrap(),
policy: FallbackPolicy::default(),
};
let posterior = Posterior::uniform(2);
let decision_id = DecisionId::from_parts(1_700_000_000_000, 42);
let trace_id = franken_kernel::TraceId::from_parts(1_700_000_000_000, 1);
let ctx = EvalContext {
calibration_score: 0.9,
e_process: 0.5,
ci_width: 0.1,
decision_id,
trace_id,
ts_unix_ms: 1_700_000_000_000,
};
let outcome = evaluate(&contract, &posterior, &ctx);
assert!(!outcome.fallback_active);