Breaking Changes

AssumptionError API Redesign

The AssumptionError / AssumptionException type has been redesigned across all 7 languages:

  • Removed functionName parameter from all factory methods. Violations are now identified solely by (id, subject) pairs, simplifying the error model.
  • New Domain assumption ID (priority 1, between Validity and Positivity). Used for parameter-level domain violations (e.g., misrate out of range, sample too small for requested precision).
  • New Misrate subject added alongside X and Y. Allows violations to report which specific parameter was invalid.
  • Priority order changed: Validity (0) > Domain (1) > Positivity (2) > Sparity (3). Previously Positivity was 1, Sparity was 2.

Migration: Update any code that constructs or pattern-matches on AssumptionError factory methods (e.g., Validity(functionName, subject) becomes Validity(subject)).

Removed Deprecated Rng.UniformInt (C#)

The Rng.UniformInt(long, long) method deprecated in v6 has been removed. Use Rng.UniformInt64 instead.

Two-Sample Bounds: Min-Misrate Domain Guard

ShiftBounds and RatioBounds now reject misrate values below the minimum achievable misrate for the given sample sizes. Previously, impossible misrate values would silently produce degenerate bounds. This will surface as a Domain error for callers passing very small misrate with small samples.

RatioBounds: Domain Check Before Positivity

RatioBounds now checks the Domain assumption (misrate validity) before checking Positivity. This changes which error is reported first when both violations are present.

Go Number Constraint Expanded

The Number generic constraint now includes unsigned integer types (uint, uint8, ..., uint64). This is technically additive but may affect type inference in edge cases.

Test Data Overhaul

Cross-language reference test data has been substantially reorganized:

  • Removed: tests/assumptions/ directory and all assumption-specific test suites. Error cases are now co-located with each estimator's test data using expected_error fields.
  • Regenerated: shift-bounds/ and ratio-bounds/ test files with larger minimum sample sizes (5 instead of 1-3) and new error test cases.
  • Renamed: Many test files to follow the new naming taxonomy (natural-5-5 instead of natural-3-2, etc.).

New Features

CenterBounds Estimator

Exact distribution-free confidence bounds for the Hodges-Lehmann pseudomedian (Center). One-sample analog of ShiftBounds/RatioBounds, based on Wilcoxon signed-rank theory. Uses the FastCenterQuantiles algorithm to extract order statistics from the implicit pairwise average matrix without materializing all N(N+1)/2 pairs.

from pragmastat import center_bounds
bounds = center_bounds([1, 2, 3, 4, 5, 6, 7, 8, 9, 10], 0.05)
# Bounds(lower=3.5, upper=7.5)

Available in all 7 languages: CenterBounds (C#), center_bounds (Python/Rust/R), centerBounds (TypeScript/Kotlin/Go).

SignedRankMargin Function

Computes the critical margin for one-sample signed-rank bounds, analogous to PairwiseMargin for two-sample bounds. Uses exact Wilcoxon signed-rank distribution via dynamic programming for n <= 63 and Edgeworth approximation for larger n.

from pragmastat import signed_rank_margin
margin = signed_rank_margin(10, 0.05)  # 18

Available in all 7 languages.

Rng.resample (Bootstrap Sampling)

New method for sampling with replacement (bootstrap resampling):

from pragmastat import Rng
rng = Rng("demo-resample")
rng.resample([1, 2, 3, 4, 5], 7)  # [5, 1, 1, 3, 3, 4, 5]

Available in all 7 languages. Cross-language reproducibility verified via reference test data in tests/resample/.

GaussCdf Shared Module

Extracted standard normal CDF implementation (ACM Algorithm 209) into a shared internal module, replacing code previously inlined in PairwiseMargin. Now shared between PairwiseMargin and SignedRankMargin.

Bug Fixes

  • fast_spread bounds corruption under parallel load (Rust): Changed internal index types from usize to u32/u64 to prevent overflow with large inputs. Added bounds-checking guards and fixed accumulator types for count variables.
  • Multiplic quantile guards (C#): Removed overly aggressive epsilon guards (1e-9) in the quantile function. Now only returns boundary values at exact 0 and 1.
  • Go Median overflow: Now computes (float64(a) + float64(b)) / 2 instead of float64(a + b) / 2, preventing integer overflow with large values.

Infrastructure

Version Management

  • Version is now managed via a single VERSION file at the repository root, replacing the previous manual/version.typ approach.
  • New mise run version <ver> task propagates the version to all language manifests.
  • New mise run publish <ver> task bumps version, pushes, and triggers the publish workflow.

CI Improvements

  • Added ci-success gate job aggregating all language CI results for branch protection.
  • Added cs:check step to C# CI pipeline.
  • Go CI now uses mise tasks (go:check, go:test) instead of raw commands.
  • Dev branch excluded from push-triggered CI.

Publish Workflow

  • Publish is now triggered via workflow_dispatch instead of tag push.
  • Tags are created programmatically during the publish job.
  • Version is read from VERSION file instead of parsed from Typst source.

TypeScript: ESLint 8 to 9 Migration

Migrated from .eslintrc.js (legacy config) to eslint.config.js (flat config). ESLint upgraded from v8 to v9, @typescript-eslint packages upgraded from v6 to v8.

Tooling

  • Replaced deprecated serde_yaml with serde_yml in the tools crate.
  • Removed deprecated UniformInt methods from Rust RNG internals.
  • Added typst to mise-managed tools.
  • Downgraded Java toolchain from Temurin 23 to Temurin 11 (Kotlin compatibility).
  • Added ts:restore as dependency for ts:build.
  • Fixed cs:clean to not run dotnet clean (which requires restore).
  • Added r:restore as dependency for r:check and r:ci.

Simulation Infrastructure

  • New Rust simulation crate (rs/pragmastat-sim): Parallel simulation runner with indicatif progress bars, supporting avg-drift, disp-drift, center-bounds, shift-bounds, and ratio-bounds simulations.
  • C# simulation improvements: Split monolithic coverage simulation into 4 dedicated commands, migrated to Rng-based sampling with string seeds for reproducibility, switched to Release builds, added AssumptionException recording in output, 10x default sample count.
  • New mise tasks: rs:sim, rs:sim:all, cs:sim:all.

Documentation

  • New manual sections: one-sample bounds methodology, CenterBounds toolkit reference, SignedRankMargin toolkit reference, resample toolkit reference.
  • New algorithm documentation: FastCenterQuantiles, FastSignedRankMargin.
  • New study: bootstrap vs signed-rank approaches for center bounds.
  • New study: median bounds efficiency analysis.
  • Test suite documentation added for center-bounds and signed-rank-margin.
  • Comprehensive tests/README.md documenting test file format, naming taxonomy, and tolerance values.
  • Updated AGENTS.md API list to reflect new one-sample bounds and margin functions.

Test Data

New test suites:

  • tests/center-bounds/ (40 files): natural, symmetric, asymmetric, additive, uniform, edge cases, properties, error cases
  • tests/signed-rank-margin/ (40 files): exact, medium-n approximation, boundary, demo, error cases
  • tests/resample/ (16 files): cross-language bootstrap reproducibility

Removed:

  • tests/assumptions/ (4 files): assumption error tests moved inline to each estimator's test suite using expected_error fields

Regenerated with larger minimum sample sizes:

  • tests/shift-bounds/ and tests/ratio-bounds/: minimum sample size increased from 1-3 to 5, new error test cases for min-misrate domain guard

Full Changelog: https://github.com/AndreyAkinshin/pragmastat/compare/v6.0.1...v7.0.0