docs: record that the event log is the source of truth, and why
#45 asked whether a fitted `History` can be persisted, and noted that the absence of `serde` "reads as an omission rather than a decision, which invites exactly this issue from every consumer in turn". Fair. Documents the decision on `History` along with the reasoning that makes it one: `converge` reaches a fixed point determined by the events, ratings and configuration alone, so a snapshot would carry no information the event log does not — it would cache the computation, never the answer. It also bounds what a snapshot could buy, since that is the question a consumer actually has. Re-converging an unchanged history costs one iteration, measured at 0.91 ms against 365 ms cold on 2 000 events, so it would make a cold restart cheap and do nothing for appends. Appending one event moves its participants more than a sigma across their whole history, so that re-convergence is real work rather than repeated work. Closes #45 Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_011hcFjNDmHXZF8URGLku5zZ
This commit is contained in:
@@ -189,6 +189,32 @@ impl CompetitorConfig {
|
||||
}
|
||||
}
|
||||
|
||||
/// A fitted history: competitors, their skills over time, and the events that
|
||||
/// produced them.
|
||||
///
|
||||
/// # Persistence
|
||||
///
|
||||
/// `History` is deliberately not serializable, and the event log is the
|
||||
/// intended source of truth. This is a decision rather than an omission.
|
||||
///
|
||||
/// [`converge`](History::converge) reaches a fixed point determined by the
|
||||
/// events, ratings and configuration alone — not by the message state it
|
||||
/// started from, which ingestion resets anyway. So a snapshot of a fitted
|
||||
/// history would carry no information the event log does not; it would be a
|
||||
/// cache of the *computation*, never of the answer.
|
||||
///
|
||||
/// That also bounds what one could buy. Re-converging an unchanged history
|
||||
/// costs a single iteration — measured at 0.91 ms against 365 ms cold on 2 000
|
||||
/// events — so a snapshot would make a cold restart cheap. It would do nothing
|
||||
/// for appends: adding one event moves its participants by more than a sigma
|
||||
/// across their whole history, back to their first appearance, so the
|
||||
/// re-convergence is real work rather than repeated work. Cost per append is
|
||||
/// inherently O(history). A design that made appends cheap would be computing a
|
||||
/// filtering estimate — see [`History::filtered_learning_curve`] — rather than
|
||||
/// Through Time.
|
||||
///
|
||||
/// `tests/reconvergence_equivalence.rs` pins the path-independence this rests
|
||||
/// on.
|
||||
pub struct History<
|
||||
T: Time = i64,
|
||||
D: Drift<T> = ConstantDrift,
|
||||
|
||||
Reference in New Issue
Block a user