# Changelog > What changed, why, and which of it was a correction to something previously wrong. Version history for the `quant-research` server. Statistical corrections are listed as corrections, including the ones that mean an earlier number was wrong. ## 0.2.0 — 2026-08-28 ### Multi-tenancy Every tenant-scoped table carries owner_id and is protected by Postgres row-level security, ENABLED and FORCED. Isolation is not implemented in query filters, so a tool that forgets to scope returns nothing rather than another tenant's rows. `holdout_one_shot` is unique on (owner_id, strategy_id) — scoped to strategy_id alone, one tenant could have spent another's only attempt. Family names, spec hashes and client_request_ids are per-tenant. Market data is deliberately shared; a split freezes the gap policy in force when registered. Principals and API keys: `quant-mcp principal add `; keys are stored as sha256 digests. ### Cross-sectional testing (new tools) - data_ingest_universe / universe_catalog — backfill and register a basket. - backtest_cross_section — one spec across a universe, counted as ONE trial. - stats_effective_tests — M_eff from the asset correlation eigenspectrum. All three estimators are reported rather than reduced to one, because the safe direction depends on the use: take the LARGER value when raising a multiplicity hurdle and the SMALLER when widening an interval. On this venue the same 22 pairs give 3.0 by participation ratio and 6.8 by entropy, so no single one of them is the cautious answer. Pooled standard errors use M_eff, not the asset count. ### Honest uncertainty (new tools) - stats_bootstrap_ci — stationary-bootstrap intervals on Sharpe and expectancy. - stats_triple_barrier — labels candidate entries by which barrier hits first; reports a breakeven hit rate that exceeds 1.0 when barriers are tighter than the round-trip fee. - data_resample — dollar and volume bars, with a before/after normality comparison rather than an assumption that resampling helped. ### Statistical corrections (independent review, gpt-5.6) - Purging now covers the RIGHT edge of each test block: test labels run to hi+h, so training events after the block up to hi+h overlap them. Purging only backwards leaked that edge whenever the label horizon was positive. - CSCV uses equal subsets; unequal ones break the symmetric construction PBO is defined on. - M_eff no longer uses the Kaiser criterion, which reports 1 for perfectly independent assets (with C = I every eigenvalue is exactly 1). Participation ratio and entropy both satisfy M_eff(independent)=k and M_eff(identical)=1. - Taking the SMALLEST M_eff was documented as conservative; for a maximum-over-trials hurdle it is anti-conservative, because E[max SR] rises with N. The two directions are now reported separately. - Bootstrap p-values use (1 + count)/(B + 1), so a finite number of replications can no longer report exactly zero. - Romano-Wolf reports the full rejection count alongside the truncated display. - Cross-sectional dispersion is labelled descriptive, not a standard error. ### Correctness - Fees read fee_tier_without_promotion.current_tier, not the promotional fee_tier object, and carry a one-hour staleness horizon. - Candles moved from Coinbase Exchange to Advanced Trade (350/request, not 300) so prices and fees describe the same venue; gate_check warns on a mismatch. - Dataset provenance accumulates per pull instead of being overwritten by each top-up. - Transport is stateless HTTP, so a redeploy no longer kills connected clients. Corollary: a connected client cannot be told about NEW tools; reconnect to refresh the tool list. ### Findings recorded - 22 liquid Coinbase USD pairs -> M_eff 3.0 (participation) / 6.8 (entropy). - Family 'btc-ma' fails cross-sectionally: pooled Sharpe -1.376 over 10 assets (run 3e2ab9ed), 3 of 10 positive, best AERO-USD at 1.19. Spearman correlation between per-asset Sharpe and trade count: -0.61, so the ranking tracks turnover rather than signal. Trial counts are read from the ledger, never quoted here; they are per-tenant and monotonic.