# Changelog All notable changes to this project will be documented in this file. ## Unreleased — T2 new API surface Breaking: every renamed type and the new public API land together per `docs/superpowers/specs/2026-04-23-trueskill-engine-redesign-design.md` Section 7 "T2". ### Breaking renames - `Batch` → `TimeSlice` - `Player` → `Rating` (and the `.player` field on `Competitor` is now `.rating`) - `Agent` → `Competitor` - `IndexMap` → `KeyTable` - `History` field `.batches` → `.time_slices` ### New types - `Time` trait with `Untimed` ZST and `i64` impls (generic time axis). - `Drift` — generified from the old `Drift` trait. - `Event`, `Team`, `Member` — typed bulk-ingest event shape. - `Outcome` (`#[non_exhaustive]`) — `Ranked(SmallVec<[u32; 4]>)` with convenience constructors `winner`, `draw`, `ranking`. `Scored` lands in T4. - `Observer` trait + `NullObserver` ZST — structured progress callbacks. - `ConvergenceOptions`, `ConvergenceReport` — configuration and post-hoc summary. - `GameOptions`, `OwnedGame` — ergonomic Game constructors without lifetime gymnastics. - `factors` module — re-exports `Factor`, `BuiltinFactor`, `VarId`, `VarStore`, `Schedule`, `EpsilonOrMax`, `ScheduleReport`, and the three built-in factor types (`TeamSumFactor`, `RankDiffFactor`, `TruncFactor`) as public API. ### New `History` API - Three-tier ingestion: - Tier 1 (bulk): `add_events>>(events) -> Result` - Tier 2 (one-off): `record_winner(&K, &K, T)`, `record_draw(&K, &K, T)` - Tier 3 (fluent): `event(T).team([...]).weights([...]).ranking([...]).commit()` - `converge() -> Result` — replaces `convergence(iters, eps, verbose)`. - `current_skill(&K)`, `learning_curve(&K)`, `learning_curves()` (now keyed on `K`). - `log_evidence()` zero-arg, `log_evidence_for(&[&K])`. - `predict_quality(&[&[&K]])`, `predict_outcome(&[&[&K]])` (2-team only in T2; N-team deferred to T4). - `intern(&Q)` / `lookup(&Q)` expose the internal `KeyTable` for power users. - `History` is now fully generic with defaults ``. ### New `Game` API - `Game::ranked(&[&[Rating]], Outcome, &GameOptions) -> Result`. - `Game::one_v_one(&Rating, &Rating, Outcome) -> Result<(Gaussian, Gaussian), _>`. - `Game::free_for_all(&[&Rating], Outcome, &GameOptions) -> Result`. - `Game::custom(...)` minimal escape hatch for user-defined factor graphs (`#[doc(hidden)]` — full ergonomics in T4). - `Game::log_evidence()` and `OwnedGame::log_evidence()` accessors. ### Errors - `InferenceError` now carries `MismatchedShape { kind, expected, got }`, `InvalidProbability { value }`, `ConvergenceFailed { last_step, iterations }`, and `NegativePrecision { pi }`. Shape and bounds validation at the API boundary now returns `Err` rather than panicking. ### Removed (breaking) - `History::convergence(iters, eps, verbose)` — use `converge()`. - `HistoryBuilder::gamma(f64)` — use `.drift(ConstantDrift(g))`. - `HistoryBuilder::time(bool)` and `History.time: bool` — use the `Time` type parameter. - The nested-`Vec>>` public `add_events` signature — use typed `add_events(iter)`. - `learning_curves_by_index()` — use `learning_curves()`. ### Performance `Batch::iteration` bench: **21.36 µs** (T1 was 22.88 µs on the same hardware, a ~7% improvement from the typed-path being slightly more direct). Gaussian operations unchanged. ### Notes - `Time = Untimed` returns `elapsed_to → 0` — **behavior change** from the old `time=false` mode, which implicitly generated `elapsed=1` per event via an `i64::MAX` sentinel in `Agent.last_time`. Tests that relied on the old `time=false` semantics now use `History::` with explicit `1..=n` timestamps. ## 0.1.0 - 2026-04-23 ### Features - feat: added a Drift trait and a "default" ConstantDrift implementation ### Miscellaneous Tasks - chore: added cliff.toml, release.toml and rustfmt.toml - chore: clean up ### Other (unconventional) - Initial commit. - Begin working on batch. - Passing tests for Batch - Working on History struct. First test is passing. - More test passing for History - Added more functions to History - Remove Display impl, better to use Debug - Use flatten instead of flat_map - Handle case where there is no time - It works, or so it seems - Use PlayerIndex instead of String - Inline a lot of functions - Refactor some code - Refactor some stuff - Port from julia version instead - More things, better things, awesome - More tests, more code - More things, more tests - Fix tests - More tests - More tests - Added builder for History, and start migrating test to use builder instead. - Update test to use builder - Remove unused code - Use and Index struct instead of str and String for player id - Update example so now it works, and thats, well, good - Update test to use assert_ulps_eq - Fixed test - Change time to use i64 instead of u64 - Small change - Clean up example - Update crates and added methods to get a key or all keys in an IndexMap - Added a get function to IndexMap - Agents doens't have to be behind a mutable reference in within_prior - Agents doens't have to be behind a mutable reference in within_priors - Refactor so we can see if there is any way to improve the performance - Fix clippy warning - More refactoring - Remove warnings and refactor some code - Added benchmark for Batch - Added default implementation for TeamMessage - Remove unused mut reference - Make it more rusty - More rustifying - Small refactor - Rename d to diff, and t to team - Added more links to readme - Fix broken link in README - Update crates - Clean up - Dry my eyes - Remove unnecessary allocations - Fix clippy warning - Refactor history - Rename variables - Move stuff around - Added quality function - Make quality a free standing function instead - Improve performance - Change assert to debug_assert - Added todo to readme, and documentation for quality function - Basic test for quality - Ignore temp folder - Update edition - Small changes for new 2024 edition - remove notepad - added benchmark ### Styling - style: cargo fmt