diff --git a/CHANGELOG.md b/CHANGELOG.md index 9027f98..ce3ed37 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,90 @@ 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 diff --git a/benches/baseline.txt b/benches/baseline.txt index 7305842..26f63ae 100644 --- a/benches/baseline.txt +++ b/benches/baseline.txt @@ -65,3 +65,36 @@ Gaussian::pi_tau_combined 234.xx ps (unchanged) # - Gaussian operations unchanged vs T0. # - All 53 tests pass. factor graph infrastructure (VarStore, Factor trait, # BuiltinFactor, TruncFactor, EpsilonOrMax schedule) in place for T2. + +# After T2 (2026-04-24, same hardware) + +Batch::iteration 21.36 µs (1.07× vs T1 22.88 µs — 7% improvement) +Gaussian::add 218.97 ps (unchanged) +Gaussian::sub 218.58 ps (unchanged) +Gaussian::mul 218.59 ps (unchanged) +Gaussian::div 218.57 ps (unchanged) +Gaussian::pi 264.20 ps (unchanged) +Gaussian::tau 260.80 ps (unchanged) + +# Notes: +# - API-only tier; hot inference path unchanged. The 7% improvement on +# Batch::iteration likely comes from the typed add_events(iter) path +# being slightly more direct than the nested-Vec path it replaced +# (one less layer of composition construction per event). +# - Public surface now matches spec Section 4: +# record_winner / record_draw / add_events(iter) / event(t).team().commit() +# converge() -> Result +# learning_curve(&K) / learning_curves() / current_skill(&K) +# log_evidence() / log_evidence_for(&[&K]) +# predict_quality / predict_outcome +# Game::ranked / one_v_one / free_for_all / custom +# factors module (pub Factor/Schedule/VarStore/EpsilonOrMax/BuiltinFactor) +# - Breaking type renames: Batch→TimeSlice, Player→Rating, Agent→Competitor, +# IndexMap→KeyTable. +# - Generic over T: Time (default i64), D: Drift, O: Observer, +# K: Eq + Hash + Clone (default &'static str). +# - Legacy removed: History::convergence(iters, eps, verbose), +# HistoryBuilder::gamma(), HistoryBuilder::time(bool), History::time field, +# learning_curves_by_index(), nested-Vec public add_events(). +# - 90 tests green: 68 lib + 10 api_shape + 6 game + 4 record_winner + +# 2 equivalence.