Public validation defect
Protected main a7be9eb8ca0b807364173f8471fc300e3cf9350f performs caller-controlled Python conversion before package-owned validation in the observed-score equating boundary:
_infer_k(..., k, ...) returns int(k) for explicit score ceilings;
equate_observed_scores(..., method=...) passes method=str(method) to Rust and stringifies it again when constructing EquateResult.
A caller-defined __int__, __str__, or __repr__ can therefore execute or raise arbitrary exceptions before the finite equating-control vocabulary and score-ceiling domain are established. This is a public API error-surface/reliability boundary; it does not imply a defect in the Rust equating arithmetic.
Bounded test-first slice
Start from exact protected main and preserve all accepted numerical semantics.
- Add a fail-first public regression using an otherwise valid tiny X/Y score pair and a hostile non-string
method object whose __str__/__repr__ raise. The package must reject it with a stable non-reflective ValueError before compiled equating work.
- Add a fail-first regression for explicit
k_x/k_y using a hostile non-integer object whose __int__/__index__/__repr__ raise. Reject before Rust with a package-owned finite score-ceiling error.
- Preserve built-in string method identities and documented aliases exactly; do not silently invent a new method vocabulary. Preserve ordinary Python and genuine NumPy integer ceilings in the documented non-negative domain.
- Reject booleans, fractional/non-integer ceilings, negative ceilings, and unsupported/non-string methods without reflecting caller content or executing conversion/representation hooks.
- Validate semantic controls before expensive/result-producing Rust work. Tests must install a sentinel compiled-core callable and prove rejected controls never reach it.
- Preserve accepted-input score arrays, inferred-ceiling behavior, Rust-owned mean/linear/equipercentile equations, result values, and caller-array immutability.
- Keep this first slice limited to
equate_observed_scores plus its shared explicit-ceiling helper. NEAT method/anchor_kind/w1, loglinear degree and kernel controls require separately bounded follow-ups after this public helper is accepted; do not silently widen.
- After product GREEN add concise doctoring grounded in Python 3.14's supported built-in/data-model conversion semantics plus the existing Kolen & Brennan (2014) scientific basis, an authoritative changelog fragment, exact changed-production statement/branch coverage and complete public docs; deterministically render/check
CHANGELOG.md before Ready.
- Require one unchanged final head with focused/full Python, Rust/PyO3/package/GPU-existing/fuzz/Security/SAST/current-head review and repository governance evidence.
Writer/dependency boundary
Fresh open-PR search found no active PR changing python/fast_mlsirm/equating.py; #663 is a distinct fixed-anchor IRT linking Rust-ownership test and currently changes only its linking ownership regression. Refetch exact head/main/target blob/open writer evidence before every mutation and abort if another equating writer appears. Canonical cross-cutting documentation remains #604/#621.
Scientific boundary
This patch changes validation order and error ownership only. All observed-score equating arithmetic remains Rust-owned. It must not alter the estimand, continuization, score-distribution handling, or interpretation, and it makes no validity/equating-design claim beyond the existing documented contract.
Public validation defect
Protected
maina7be9eb8ca0b807364173f8471fc300e3cf9350fperforms caller-controlled Python conversion before package-owned validation in the observed-score equating boundary:_infer_k(..., k, ...)returnsint(k)for explicit score ceilings;equate_observed_scores(..., method=...)passesmethod=str(method)to Rust and stringifies it again when constructingEquateResult.A caller-defined
__int__,__str__, or__repr__can therefore execute or raise arbitrary exceptions before the finite equating-control vocabulary and score-ceiling domain are established. This is a public API error-surface/reliability boundary; it does not imply a defect in the Rust equating arithmetic.Bounded test-first slice
Start from exact protected main and preserve all accepted numerical semantics.
methodobject whose__str__/__repr__raise. The package must reject it with a stable non-reflectiveValueErrorbefore compiled equating work.k_x/k_yusing a hostile non-integer object whose__int__/__index__/__repr__raise. Reject before Rust with a package-owned finite score-ceiling error.equate_observed_scoresplus its shared explicit-ceiling helper. NEATmethod/anchor_kind/w1, loglinear degree and kernel controls require separately bounded follow-ups after this public helper is accepted; do not silently widen.CHANGELOG.mdbefore Ready.Writer/dependency boundary
Fresh open-PR search found no active PR changing
python/fast_mlsirm/equating.py; #663 is a distinct fixed-anchor IRT linking Rust-ownership test and currently changes only its linking ownership regression. Refetch exact head/main/target blob/open writer evidence before every mutation and abort if another equating writer appears. Canonical cross-cutting documentation remains #604/#621.Scientific boundary
This patch changes validation order and error ownership only. All observed-score equating arithmetic remains Rust-owned. It must not alter the estimand, continuization, score-distribution handling, or interpretation, and it makes no validity/equating-design claim beyond the existing documented contract.