feat(outcome): per-event score_sigma override on Outcome::Scored

Outcome::Scored shape changes from tuple to struct:
{ scores, sigma: Option<f64> }. New constructor scores_with_sigma
sets sigma=Some(s) and debug-asserts s > 0.0; existing scores(I)
constructor keeps its signature and builds with sigma=None internally.
team_count, as_scores, as_ranks accessor pattern matches updated.

History::add_events resolves sigma.unwrap_or(self.score_sigma) at the
ingest arm, so downstream EventKind::Scored stays a plain f64 and
TimeSlice / run_chain need zero changes.

Breaking change to the public Outcome::Scored variant shape
(acceptable in 0.1.x). Bit-equal for callers using the no-override
path because the resolution falls through to self.score_sigma exactly
as before.
This commit is contained in:
2026-05-08 21:27:09 +02:00
parent d1d6b5136c
commit b46e7f068d
2 changed files with 69 additions and 12 deletions
+7 -2
View File
@@ -732,9 +732,14 @@ impl<T: Time, D: Drift<T>, O: Observer<T>, K: Eq + Hash + Clone> History<T, D, O
kinds.push(EventKind::Ranked);
ranks.iter().map(|&r| max_rank - r as f64).collect()
}
crate::Outcome::Scored(scores) => {
crate::Outcome::Scored { scores, sigma } => {
let resolved = sigma.unwrap_or(self.score_sigma);
debug_assert!(
resolved > 0.0,
"resolved score_sigma must be > 0.0 (got {resolved})"
);
kinds.push(EventKind::Scored {
score_sigma: self.score_sigma,
score_sigma: resolved,
});
scores.to_vec()
}