Skip to content

Changelog

Changelog

All notable changes to this project are documented in this file.

The format is based on Keep a Changelog, and this project adheres to Semantic Versioning.

[1.13.2] - 2026-10-12

Fixed

  • Response-style, paired-item, and reverse-scoring scale options now reject Boolean lookalikes, text, nonscalar arrays, NaN, and values that overflow during float conversion with consistent ValueError messages. Numeric scalars, zero-dimensional arrays, Decimal, and Fraction remain supported, and exact integer bounds stay exact.
  • midpoint_responding() now applies the same scalar contract to tolerance instead of accepting Booleans or leaking NumPy errors for malformed values.

[1.13.1] - 2026-10-05

Fixed

  • Shared threshold and percentile validation now rejects NumPy booleans, nonscalar arrays, and numeric values that overflow during float conversion with consistent ValueError messages. Zero-dimensional numeric arrays and numeric strings remain supported.
  • lz_flag() and individual_reliability_flag() now require finite thresholds before running their scoring work instead of silently producing misleading flags for NaN or infinite cutoffs.

[1.13.0] - 2026-10-05

Added

  • Five opt-in, composite-enabled registry indices bring the catalog to 26: avgstr, autocorrelation, gpoly, u3poly_fit, and ht.
  • avgstr scores the average length of identical runs, matching careless::longstring(avg = TRUE), through longstring_scores(avg=True). With na_rm=True, a run can continue across a skipped item.
  • autocorrelation() and autocorrelation_flag() detect zigzag, seesaw, and short cyclic patterns (Gottfried et al., 2022; responsePatterns::rp.acors) from the largest ("max_abs") or summed ("sum_abs") absolute correlation of each respondent's responses with lagged copies; return_lags=True also returns the strongest lag. As in R, a lag whose window does not vary scores 1. The default max_lag=10 departs from R's J - 3 deliberately, raising the AUC in a simulated 40-item survey from 0.75 to 0.96. Registry and CLI settings: IndexOptions.autocorrelation_max_lag/autocorrelation_statistic and --autocorrelation-max-lag N (none for the R range)/ --autocorrelation-statistic. A 23,000-by-40 matrix scores in about 16 ms.
  • PerFit's item-step person-fit statistics: gpoly() (polytomous Guttman errors, normalized by default as Gnormed.poly), u3poly() (polytomous U3), and ht() (transposed scalability for dichotomous items), using exact integer arithmetic so perfect patterns score exactly 0. The registry indices gpoly and u3poly_fit flag high and ht flags low, with gpoly_flag(), u3poly_flag(), ht_flag(), and IndexOptions.person_fit_ncat/ --person-fit-ncat. gpoly() and u3poly() scored 22,000 respondents by 30 five-category items in 4–15 ms, against 19–53 ms for guttman().
  • response_time_effort() computes response time effort (Wise & Kong, 2005), the share of answered items whose time reaches an item threshold: normative NT10 values by default (normative_fraction=0.10 of each item's mean time, optionally capped by max_threshold), or one shared or per-item value. return_item_flags=True also returns the rapid-response matrix, and response_time_effort_flag() flags RTE strictly below a fixed cutoff (default 0.90).
  • ier response-time --metric effort scores RTE with --effort-fraction and --effort-max-threshold, or --effort-threshold for one shared item time. It flags RTE strictly below 0.90 unless --threshold (a proportion from 0 to 1, also strict) is given. Timing archives accept the effort metric without a schema version change, so ier response-time-scores can reflag it.
  • evenodd(..., method="halves") implements the classic even-odd index (Johnson, 2005; Meade & Craig, 2012; Curran, 2016), correlating odd- and even-position factor means across factors with a clamped Spearman–Brown correction. Scores equal -careless::evenodd to within 1e-7, and respondents whose half means do not vary are unavailable, as in careless. In a simulated six-scale survey it separated random responders with an AUC of about 0.95, against about 0.5 for the unchanged default "item_pairs". Set it with IndexOptions.evenodd_method or --evenodd-method halves.
  • individual_reliability(..., factors=[...]) randomly halves each scale and correlates half means across scales (Curran, 2016; Huang et al., 2012), reaching an AUC of about 0.99 in the same simulation. Reliability functions accept an np.random.Generator as random_seed, and IndexOptions.reliability_factors/--reliability-factors 8,8,8 configure the registry index.
  • psychsyn(), psychant(), psychsyn_critval(), and psychsyn_summary() accept item_correlations="pairwise", the careless-compatible missing-data mode: pairs come from pairwise-complete correlations over at least three shared respondents, and each respondent is scored over the pairs they answered. IndexOptions.psychsyn_item_correlations and --psychsyn-item-correlations pairwise apply it to the registry. At 1% missing cells, a 2,000-by-20 Likert matrix scores at least 95% of respondents, where the unchanged "complete" default scores none.
  • infrequency() and infrequency_flag() accept keyword-only acceptable_ranges, one inclusive (low, high) range per item with -inf/inf for open ends, as an alternative to expected_responses. Integer responses are compared exactly. Registry and CLI settings: IndexOptions.infrequency_acceptable_ranges and --infrequency-acceptable-ranges '1:2,5:' (attached form for negative values, as in --infrequency-acceptable-ranges=-3:-1).
  • Split IRV in screen(), composites, and the CLI through IndexOptions.irv_num_split/irv_split_points and --irv-num-split N/ --irv-split-points 0,10,20; invalid settings are irv index failures.
  • reverse_score(x, items, scale_min=None, scale_max=None) recodes reverse-keyed items as scale_min + scale_max - x, inferring missing bounds from the whole matrix and rejecting out-of-range responses. Integer input keeps its dtype when it holds both bounds and is recoded exactly.
  • IndexOptions.reverse_keyed_items and --reverse-keyed-items 1,4,7 reverse-score the listed 0-based columns only for indices that assume one keying direction (evenodd, individual_reliability, guttman, lz, gpoly, u3poly_fit, ht); sequence, response-style, pair, outlier, and attention-check indices read responses as presented. With a third of the items reverse-worded, classic even-odd's AUC rose from 0.62 to 0.96 while longstring kept 1.00 in the same run. With person_fit_ncat, gpoly and u3poly_fit reflect items on their declared category scale. Invalid items or bounds fail each keyed index with a reverse_keyed_items could not be applied message.
  • psychsyn_flag(), psychant_flag(), person_total_flag(), u3_poly_flag(), and midpoint_responding_flag() return (scores, flags) with default percentiles that reproduce screen(). The last two take scale bounds before threshold and percentile, as acquiescence_flag() does. psychsyn_critval(), psychsyn_summary(), and mahad_summary() are now exported, and both summaries also report n_total, n_valid, and n_missing; their previous count keys remain.
  • composite_scores() and composite_scores_summary() accept unsupported="drop", and ier composite-scores accepts --skip-unsupported, so screen() results and screening archives combine without filtering. Indices that are not composite-enabled (by default u3_poly, midpoint, acquiescence) are dropped, and the CLI names them.
  • index_agreement(screen_result, kind) compares every pair of selected indices by co-flag "overlap" counts, flag "jaccard" similarity (the default), or "spearman" rank correlation oriented by flag direction, with NaN for undefined cells. plot_index_agreement() draws the matrix.
  • screen_table() and composite_table() return ordered, respondent-aligned column mappings following the CLI CSV schemas, ready for pandas.DataFrame(table, index=df.index) or polars.DataFrame(), without copying result arrays.
  • save_screen_archive() and load_screen_archive() persist complete screen() results in the ier screen --format npz schema, so the restored ScreenResult (in a new ScreenArchive) can be plotted or tabulated without the responses. The writer validates every decision and replaces the file atomically; the loader recomputes flags from scores and recorded cutoffs, avoiding the tie flips of passing percentile cutoffs to screen_scores().
  • compression_level= on every archive writer and --compress-level on all six CLI scoring/replay commands select DEFLATE levels 1–9 for compressed output.
  • plot_composite() draws a composite score histogram with an optional shaded cutoff, overlaid flags, and the flagged count. plot_distributions() now draws each applied cutoff, shades the flagged tail, and titles panels with flagged counts; show_thresholds=False restores plain histograms.
  • --config PATH reads option values from a TOML file on screen, composite, screen-scores, composite-scores, response-time, and response-time-scores, flat or in per-command tables such as [screen]. Keys are long option names; switches take Booleans, lists take arrays, and INDEX=VALUE options take tables. Values pass through each command's own checks before data is read, with errors naming the key, section, and file. Unknown or duplicate keys and invalid TOML are rejected, and command-line options take precedence over conflicting configured ones.
  • ier inspect DATA reports how a matrix is parsed without scoring it: delimiter detection, header and column selection, shape, missing cells per item, observed range and distinct values, and suggested --scale-min=VALUE --scale-max=VALUE. It warns when an observed extreme is used by fewer than 1% of respondents, when inferred bounds are likely wrong.
  • Repeatable --item-pattern GLOB and --exclude-column NAME select items from survey exports with metadata columns on screen, composite, response-time, and inspect, as in ier screen export.csv --id-column ResponseId --item-pattern 'Q*'.
  • index_catalog() and ier indices report uses_keyed_responses and alternative_options (groups of interchangeable IndexOptions fields), and the CLI output guide documents the catalog layouts.
  • ier exports FlagDirection, FlagMode, CombineMethod, EvenOddMethod, ItemCorrelationMode, AgreementKind, IndexThresholdMap, IndexPercentileMap, IndexThresholdSource, and IndexThresholdSourceMap; ResponseTimeFlagDirection is now an alias of FlagDirection. Typed overloads on mahad(flag=...) and the diag argument of evenodd(), psychsyn(), and psychant() remove the need for casts.
  • A doctest gate runs every public module's docstring examples, with output identical under NumPy 1.26 and 2.x.
  • Benchmarks add bench_person_fit.py and options for the new modes, and every benchmark prints OPENBLAS_NUM_THREADS and OMP_NUM_THREADS. The architecture guide recommends single-threaded BLAS with workers>1: screen(workers=4) on 10,000-by-80 responses took 26–27 ms with one BLAS thread and 59–65 ms with default threading.

Changed

  • Compressed archives default to DEFLATE level 1 instead of zlib's level 6: 1,000,000-respondent screen archives saved in 1.37 s instead of 4.79 s at 27.0 MB instead of 24.7 MB. Use compression_level=6 or --compress-level 6 for the previous size.
  • plot_flagged_heatmap() aggregates to at most 1,000 respondent blocks by default (max_rows=None restores per-respondent rows), so rare flags stay visible; a block is flagged when any respondent is. order="flag_count" sorts respondents by flag count. Rendering 1,000,000-by-11 results took 0.15–0.42 s instead of 1.7–2.2 s.
  • Unseeded individual_reliability() no longer reads or advances NumPy's global random state, so it no longer follows np.random.seed(). Integer seeds keep bit-identical scores.
  • New option names make some previously unique CLI abbreviations ambiguous: --evenodd, --psychsyn, and --per on screen and composite, and --compr on every scoring command, now match several options. Scripts should spell options out in full.
  • Non-mapping thresholds, percentiles, and weights overrides raise TypeError instead of AttributeError; unregistered weight names report unknown weight index, and values too large for a float raise ValueError instead of OverflowError.
  • A bare string passed as indices raises ValueError instead of rejecting its first character, and the raw-matrix composite helpers reject indices=[] before scoring. screen(indices=[]) still returns an empty screen.
  • Missing index configuration is enforced from the catalog: semantic_syn and semantic_ant messages name only the selected index, and infrequency accepts either expected responses or acceptable ranges. IndexMetadata gains the required keys uses_keyed_responses and alternative_options.
  • Complex, datetime, timedelta, structured, and non-numeric string matrices raise ValueError("input data must contain real numeric responses") in every index. Array-likes accepted by screen(), such as collections.UserList, work in every index function; scalars, strings, mappings, sets, and generators raise a TypeError that also derives from ValueError.
  • Integer-valued options such as min_flags, workers, and min_valid_indices accept NumPy integers and other __index__ objects.
  • screen() and the composite helpers validate all selection and decision arguments before converting the response matrix, so invalid settings fail before a large matrix is copied.
  • Numeric parse failures name the data row and column, as in data row 2, column 'date' (position 3): could not convert string to float: '2024-01-01'. Archive writers report expected and received respondent-ID counts.
  • Every CLI option has help text with its default, and scoring commands group options under input, index or timing options, decision, and output headings.
  • resample_na and random_seed on psychsyn() and psychant() are documented as compatibility parameters with no effect on scores, which they never had through the public API.
  • Documentation states that u3_poly is the proportion of extreme responses, not PerFit's U3, and points to u3poly()/u3poly_fit.
  • import ier defers importlib.metadata, zipfile, and the archive stack until first use, loading 66 instead of 126 new modules after NumPy (about 16 ms instead of 27 ms).
  • The lz ability solver drops converged respondents from its Newton workspace, with bit-identical scores: bench_lz.py --categories 5 --missing-rate 0.1 fell from 0.467 s to 0.104 s, and 50,000-by-60 2PL data from 1.36 s to 0.35 s.
  • Longstring and repeating-pattern run lengths avoid a broadcast np.where: on 50,000-by-60 responses, longstring fell from 18.3 to 13.6 ms and default screening from 252 to 222 ms, with bit-identical values.
  • Markov entropy scores many-category blocks, such as 0-100 sliders, with a vectorized sort-based kernel instead of a per-respondent loop (within about 3e-14 of previous scores). With 20,000 respondents by 50 items, 101 states fell from 0.408 s to 0.034 s and 64 states from 0.367 s to 0.025 s.
  • mahad_qqplot() solves chi-square quantiles in batched Newton iterations with bit-identical results; a 20,000-by-20 Q-Q plot fell from 0.95 s to 0.063 s.
  • Compressed archive loads decompress each member once, keeping the size and CRC checks, and load 1.5–2.2x faster, matching unvalidated np.load.
  • CLI CSV output converts 1,024-row column chunks with byte-identical output: screen CSV for 100,000 respondents fell from 0.80 s to 0.27 s, and detailed composite CSV is 2.0–2.1x faster. Delimited input parses complete numeric rows with C-level float conversion, loading 100,000-by-30 matrices 1.4x faster.
  • Internal: each CLI command has its own handler with shared loading, flagging, and output; CLI index defaults come from IndexOptions(); screen and composite entry points share one validation and reduction pipeline; and coverage excludes @overload stubs. Output and scores are unchanged.
  • Source distributions include docs/*.md, and the distribution check requires them, so the bundled test suite can verify documented commands and claims against an installed wheel.

Deprecated

  • Passing indices with method="best_subset" emits a DeprecationWarning from composite(), composite_flag(), composite_summary(), and composite_probability(), and ier composite prints a warning. The indices are still ignored; this will raise in a future release.

Removed

  • The private, unused ier.psychsyn._resample_missing_correlations and ier.longstring._run_length_decode helpers.

Fixed

  • Score-changing: guttman() counted Guttman-consistent pairs instead of errors because items were ordered hardest-first. Items are now ordered easiest-first with column-order tie-breaking, so a perfect cumulative pattern scores 0 and ties no longer make scores platform-dependent. Items are ordered by their available-response means whatever na_rm is, so one missing response no longer moves an item to the hardest position. On simulated 2PL data, random responders now outrank attentive ones (AUC 0.96, previously 0.23). Guttman scores, guttman_flag(), and default screen() consensus change for most inputs.
  • Score-changing: the registry flagged and composite-weighted psychant in the wrong direction since 1.7.0, flagging the most consistent respondents and rewarding them in composites. psychant is now flagged high with a positive composite multiplier; in a simulation, the default percentile previously flagged 33 attentive respondents and no random responders.
  • Score-changing: mahad(flag=True, method="iqr" | "zscore") no longer flags unusually small distances, which with many items flagged some of the most typical respondents. screen() and composite() are unaffected.
  • pandas nullable DataFrames (convert_dtypes(), Int64/Float64/boolean with pd.NA), object columns, and nested lists with None work in every index. screen() previously soft-failed every index and returned indices_used=[], which could read as "no careless respondents".
  • Masked cells of a numpy.ma.MaskedArray (for example np.ma.masked_equal(data, -99)) are treated as missing responses instead of being scored as their underlying sentinel values.
  • longstring_pattern() requires an integer max_pattern_length of at least 2 instead of silently scoring every respondent 0 for smaller or Boolean values; the registry and CLI reject such values too.
  • mahad(confidence=...) and guttman_flag(threshold=...) reject Boolean, non-numeric, and non-finite values with ValueError.
  • Archive writers reject respondent_ids given as a set, mapping, or iterator, which could attach identifiers to the wrong respondents.
  • ier screen --format npz --min-flags N with N of 2**63 or more reports a validation error instead of an OverflowError traceback.
  • load_response_time_archive() returns canonical Boolean flags instead of passing through stored bytes other than 0 or 1.
  • Docstring examples across the public modules now match their output under NumPy 1.26 and 2.x. For example, the markov example sequence has zero conditional entropy rather than the documented 1.56, and the lz_flag() example claimed a flag its data did not produce.

[1.12.0] - 2026-10-03

Added

  • Optional streamed NPZ compression through compressed=True on both public archive writers and --compress on all six CLI scoring/replay commands. Compressed output preserves the existing pickle-free schema, decisions, identifiers, and atomic replacement behavior. Repeated-score archives in a local 100,000-respondent comparison used 92% less storage; continuous scores saved about 4%. Compression costs additional CPU and remains opt-in.

Fixed

  • Person-total correlations retain item-mean differences at subnormal response scales and near large common baselines, including mixed respondent scales and narrowly separated item groups selected by missing responses. Equal exact item means remain constant despite different cancellation orders. Independent exact-rational means and Decimal correlation tests cover missing data, layouts, batch boundaries, and bounded profile caching.
  • Archive writers reject trailing NULs in respondent IDs and failure messages before NumPy can silently truncate them. Embedded NULs remain supported. Archive readers reject invalid UTF-32 codepoints with contextual errors, including metadata whose conversion could otherwise fail inside NumPy.

Changed

  • Streaming CSV conversion removes two generator layers while preserving quoting, column selection, missing tokens, and forward-only input. Seven alternating local comparisons of 100,000-by-20 matrices showed 7–13% higher throughput with unchanged peak allocation.
  • CI validates GitHub Actions workflows and embedded shell/Python scripts with actionlint, ShellCheck, and pyflakes through the reusable lint workflow.
  • The aggregate Veto check evaluates every prerequisite dynamically, including new jobs. Tests exercise all 1,024 prerequisite outcome combinations and executable failure behavior for malformed, cancelled, and skipped results.
  • Archive benchmarks accept --compress and report file sizes alongside runtime and peak allocation to make storage/CPU tradeoffs reproducible.
  • Person-total benchmarks exercise categorical, constant, and nearby-baseline responses with scale/offset controls and exact arithmetic checks outside the timed region.

[1.11.0] - 2026-10-02

Added

  • Fresh CLI screening and composite commands accept --missing-applicable-mask for respondent-specific skip logic. Masks support headerless 0/1 text, compressed text, standard input, and read-only Boolean .npy matrices, with exact value and selected-response shape validation before scoring.
  • Repeatable --item-column NAME selects exact header names, including commas. It can be interleaved with --item-columns while preserving selection order.

Fixed

  • Response-time coefficients of variation and semantic consistency scores preserve dimensionless ratios for varying responses near floating-point underflow. Bounded row repairs use power-of-two scaling and retain missing, constant, and non-finite response behavior.
  • Public composite type annotations now distinguish default array or flag returns from diagnostic tuples, including dynamically selected diagnostics.
  • Screening archives can be replayed when every selected index soft-failed. Explicit n_respondents support in screen_scores() and save_score_archive() preserves respondent rows, identifiers, failure provenance, and zero coverage. Saved-score CLI commands carry the count automatically, including in-place archive replacement. Empty mappings without failure provenance remain invalid.
  • Corrupt or truncated gzip, bzip2, and xz survey exports report their input path through the normal CLI error handler and preserve previous output files.
  • Numeric whitespace matrices mixing spaces and tabs are detected even when delimiter sniffing initially selects tabs. Explicit tab delimiters, named metadata columns, quoted fields, and missing TSV cells retain their behavior.
  • Screening heatmaps use fixed flag colors so fully flagged and wholly unflagged cohorts remain distinct. Missing scores appear gray while absent onset detections stay unflagged. Automatic figure dimensions are bounded so large surveys can render and export without creating enormous canvases.

Changed

  • The existing CI artifact gate verifies wheel RECORD inventories, secure content hashes, and recorded sizes, and requires both WHEEL and RECORD. Real setuptools wheel regressions verify corruption whose ZIP checksums remain valid, missing manifests, malformed entries, and inconsistent file inventories. Wheel format, pure-Python tags, and build metadata must also agree with the artifact filename.
  • Strict type checks include release scripts and benchmarks. A separate public consumer gate verifies valid API calls and requires specific failures for invalid return assignments and method choices.
  • Mask input benchmarks release their temporary memory maps before deleting files, including when validation fails, so cleanup also succeeds on Windows.
  • Flag-count chart aggregation uses one histogram pass. A local one-million-row, 21-bin comparison reduced aggregation from 11.1 to 1.3 ms and peak traced allocation from 1.02 MiB to less than 1 KiB, with identical bin counts. Heatmaps also retain Boolean flags instead of constructing an extra float matrix.

[1.10.0] - 2026-10-02

Added

  • Reusable screening and composite APIs accept retained errors mappings to preserve failed selections, coverage requirements, and audit provenance. Saved-score CLI commands apply the same behavior automatically, including cutoffs and weights configured for failed indices.

Fixed

  • Attention-check scoring compares integer response categories and expected answers exactly, including adjacent int64 and uint64 endpoints, without accepting rounded neighboring categories. CLI expected-answer lists also preserve integer tokens. Missing-response policies retain their existing behavior.
  • Delimited CLI input rejects malformed quoted records and reports their physical line number before writing output. Delimiter fallback respects quoted punctuation and retains useful width errors for jagged survey exports.
  • Entirely missing delimited respondents retain their rows and alignment; physically blank lines remain skippable. This includes quoted empty single-column records and all-empty tab-delimited records.
  • Distribution validation rejects duplicate members, unsafe paths, source distribution links, stale package modules, and ambiguous metadata. Console scripts and bundled build configuration must match the project exactly. Wheel checks validate original ZIP names before Windows separator normalization or NUL truncation, as well as effective Unicode path overrides.
  • Updated locked development and documentation dependencies to urllib3 2.8.0, addressing HTTPS proxy TLS verification and chunked streaming vulnerabilities reported by the dependency audit.

Changed

  • CI runs the entire bundled source-distribution test suite against the installed wheel outside the checkout, with locked tools, warnings as errors, and the existing 95% coverage gate.
  • Integer attention checks skip unavailable NaN scans. Local 100,000 × 80 runs with 40 checks reduce runtime by 8–15% and peak traced allocation from 3.77–3.82 MiB to 3.27 MiB across pass, omit, and propagate policies.
  • Numeric CSV parsing handles empty fields directly and strips only after failed numeric conversion. Input and response-check benchmarks now verify respondent values against independent expected answers.
  • All performance benchmarks now share timing and allocation measurement, keeping allocation tracing out of timed calls and preserving measured results for correctness checks. Paired comparisons alternate operation order, and failed memory measurements always stop allocation tracing.
  • Added small CLI smoke runs for every performance benchmark and documented the common measurement method. Historical timings collected with tracing enabled must be rerun before comparing them with the new reports.
  • Benchmark comparisons discard intermediate outputs outside the timer and retain only the final repetition for correctness checks, reducing allocation bias from other retained results. Allocation samples stay alive until tracing stops so result cleanup does not inflate the recorded peak.

[1.9.0] - 2026-10-02

Added

  • ier response-time-scores reflags saved timing results or exports their exact stored decisions in text, JSON, CSV, and NPZ without the original matrix or another mixture fit. Metric, suspicious tail, missing scores, and respondent identifiers remain aligned.

Fixed

  • Calibrated Lz predictor arithmetic preserves representable results when finite difficulty and ability differences overflow, or discrimination products underflow. Strict missing-response propagation also applies when every item discrimination is zero.
  • Lz ability estimation uses scaled Newton steps for extreme finite item slopes, preserving available information without overflowing squared discriminations. Independent Decimal roots and analytic likelihood solutions verify the repair.
  • Archive readers validate NPY shapes and payload sizes before NumPy allocates arrays, rejecting inflated or overflowed shapes, nonempty zero-width members, and forged compressed sizes with contextual errors.
  • Version checks reject Unicode core digits, enforcing the ASCII SemVer grammar.
  • Prerelease version checks preserve SemVer source and tag spelling while matching normalized Python versions in lock entries and distribution metadata.

Changed

  • Distribution verification compares packaged Python sources and typing markers byte for byte with the checkout, validates runtime and optional dependency metadata, and checks every bundled project metadata field. Source distributions also retain the test fixtures required by their bundled tests. Regression tests prove failed local quality gates stop later commands.
  • The timing-reuse benchmark compares complete fresh and saved workflows with exact output parity and independent flag checks. A local 20,000 × 80 mixture run reduced runtime from 21.2 to 3.3 ms and peak allocation from 2.3 to 0.7 MiB.
  • Installed artifact smoke tests exercise timing archive reuse, including exact tie decisions, missing scores, identifiers, and atomic in-place reflagging.
  • Release, publishing, version, and documentation jobs now have explicit time limits; documentation deployment also rebuilds when the dependency lock changes.

[1.8.1] - 2026-10-01

Added

  • ier screen-scores and ier composite-scores reuse validated NPZ components for cutoff, coverage, and weighting analysis without the original item matrix. Respondent IDs, selected component order, relevant source failures, flags, probabilities, and all four output formats remain available.
  • composite_scores_summary() reports reusable composite scores, raw components, calibrated coverage, resolved weights, and aggregate statistics in one pass.
  • An end-to-end CLI reuse benchmark checks every saved result member against fresh scoring. Local 20,000 × 80 runs reduce screening from 120 to 5 ms and detailed composites from 173 to 7 ms, with peak traced allocation reduced from 7.1 MiB to 1.6–1.8 MiB.

Fixed

  • Lz dichotomization preserves exact midpoint decisions at signed and unsigned integer limits and for floating overflow, subnormal values, and rounded ties.
  • Lz scoring uses centered Bernoulli moments and scaled predictor log-odds, preserving calibrated scores near equal item probabilities without subtracting nearly equal likelihood sums. Local calibrated 10,000 × 80 runs reduce runtime by 25–36% while removing redundant logarithm matrices.
  • Logistic transforms preserve subnormal probabilities and saturated limits under strict floating-point checks, including macOS exponential underflow signals for tiny predictors. Only exponential underflow is handled locally; caller error policies remain unchanged.
  • Supplied Lz difficulty, discrimination, and ability parameters must be real one-dimensional vectors aligned to items or respondents. Invalid axes and lossy numeric types fail before scoring instead of silently broadcasting; missing parameter values retain the existing availability policy.
  • Reusable-score APIs reject Boolean, string, complex, object, and timedelta arrays instead of changing their meaning through implicit float conversion.
  • Archive loaders report malformed members, checksum failures, and corrupt ZIP containers with context, close input files on failures, and reject NPY inputs before allocating their payloads.
  • Release and publishing jobs reuse the distribution artifacts verified by CI, removing conflicting duplicate uploads and redundant builds.

Changed

  • CI explicitly selects and verifies each matrix Python version, runs the full suite with minimum and newest supported NumPy, checks benchmark typing, and validates project/lock version consistency regardless of version-bump exemptions.
  • The combined statement/branch coverage gate rises from 90% to 95%. Releases and publishing also require the existing lint, documentation, and dependency audit gates before distributing the tested artifacts.
  • Built wheel and source distribution checks verify package source completeness, metadata, and executable entry points. Isolated installations exercise real scoring, missing data, respondent IDs, compressed input, JSON, NPZ, and reuse.
  • Added hand-calculated CLI and reusable-summary checks, fault-injected archive and distribution proofs, and exact-reference Lz numerical regression tests.

[1.7.30] - 2026-09-30

Changed

  • MAD and semantic-antonym reverse scoring prove ordinary floating-point integer categories with a compact equality probe, reducing temporary memory and work. Fractional responses retain compensated subtraction, and wider integer responses retain the existing full-precision proof.
  • Added exact-reference checks for category boundaries, fractional and extreme responses, missing policies, normalized differences, and read-only layouts.

[1.7.29] - 2026-09-30

Fixed

  • Composite sums and maxima report a contextual ValueError for eligible final results made unrepresentable by an already infinite computed score. Negative infinite maximum candidates can still lose to finite winners, and respondents below the coverage minimum receive NaN without range errors.
  • Bounded reduction repairs separate infinite inputs from finite scaled arithmetic, preserving row order, coverage, and raw mean infinity semantics.
  • Mixed finite/infinite component calibration remains unavailable without arithmetic warnings. Single-component summaries count calibrated availability consistently with multiple-component reductions.

[1.7.28] - 2026-09-30

Fixed

  • MAD and semantic-antonym native arithmetic preserve small residuals when reverse scoring rounds a response away, including either pair order and exact integer scale endpoints supplied with floating responses.
  • Semantic-synonym and antonym differences normalize subnormal means before final rounding, retaining finite ratios that would otherwise become zero.
  • Native normalized differences receive exact row repairs when their final ratio is subnormal or overflows.

Changed

  • Finite reflections share exact endpoint-sum preparation across native and overflowing paired reductions.
  • Added a reflected-residual benchmark profile checked against exact sums.

[1.7.27] - 2026-09-30

Fixed

  • MAD and semantic-antonym overflow repairs preserve small finite differences when another respondent in the same block has an overflowing pair difference. Cancelled response bounds no longer force small responses into extreme units.
  • Overflowing reflections match ordered responses to ordered scale endpoints, retaining small differences near a widely separated endpoint.
  • Exceptional pair means retain rounded reflection residuals and correctly normalize subnormal differences, including cases where the scaled divisor overflows or underflows. Exact scalar repairs precede final float rounding.

Changed

  • The item-pair benchmark checks measured scores against exact sums and Decimal deviations. Its mixed-scale profile combines small responses with extreme neighbors to exercise the overflow repairs.

[1.7.26] - 2026-09-30

Changed

  • Composite reductions reuse component calibration's availability checks. Complete components use unmasked accumulation and scalar coverage increments; raw maximum reductions without coverage controls omit unused availability checks.
  • Composite cancellation checks use boolean comparisons instead of allocating an absolute-value float buffer. Sums retain a presence vector only while every processed component has missing observations.
  • Added score, coverage, component-order, and input-ownership checks for mixed complete and missing components, including unavailable component calibrations.

[1.7.25] - 2026-09-30

Fixed

  • Composite sums retain representable weighted totals after intermediate overflow, preserve tiny cancellation residuals, and round subnormal totals after combining contributions.
  • Composite maxima discard overflowing negative contributions when another component provides a finite winner.
  • Final weighted sums and maxima outside the finite float range raise a contextual ValueError. Respondents below the requested coverage minimum still receive NaN without a range error.

Changed

  • All composite reductions share component calibration and bounded numeric repairs. Single-component sums and maxima reuse an owned score buffer instead of allocating a separate accumulator.
  • Documented sum and maximum benchmark runs, including subnormal weighted totals.

[1.7.24] - 2026-09-30

Fixed

  • Composite means retain finite scores with huge or tiny positive weights, overflowing raw totals, subnormal weighted products, and severe cancellation. Missing components still omit their weights, including respondents whose only available component has a tiny weight relative to the other selected indices.
  • Single-component means omit the cancelling weight multiplication and division, while preserving direction, sparse calibration, coverage, and input ownership.

Changed

  • Component calibration and mean reduction share an internal numeric module. Safe common weight scaling keeps ordinary weighted means on the fast path; unstable reductions use bounded respondent batches with separately scaled numerator products and weight totals. Exceptional cancellation and subnormal rounding use exact scalar arithmetic.
  • Unweighted means reuse requested coverage counts as their denominator, reducing duplicate allocation in composite summaries.
  • The orchestration benchmark accepts a finite positive weight scale to validate extreme weight units and sparse coverage outside measurement.

[1.7.23] - 2026-09-29

Fixed

  • Sample percentile thresholds retain finite linear interpolation between opposite extreme scores and correctly round subnormal ties. Integer endpoint differences remain exact, and float32 endpoints interpolate in double precision. Fixed-cutoff equality, sample-relative tie exclusion, and missing-score policies remain unchanged.
  • Threshold flags compare large integers and single-precision scores against the reported cutoff without first rounding the scores or cutoff to a coarser dtype.

Changed

  • Percentile selection partitions one owned observation buffer instead of copying through a missing-aware percentile reduction. Endpoint percentiles reduce directly without copying the score vector.
  • The flagging benchmark checks flags against an independent sorted Decimal reference and covers constant, nearly constant, extreme, and all-missing scores.

[1.7.22] - 2026-09-29

Fixed

  • Composite standardization keeps constant decimal components at zero, retains tiny and nearly constant score differences, and avoids overflowing on finite large scores. Missing scores and the existing single-observation policy remain unchanged.
  • Screening, composite, and shared index summaries use stable mean, deviation, and median reductions, including double-precision accumulation for float32 inputs and exact integer totals. Extreme finite scores retain representable mean, deviation, and median values without intermediate overflow or underflow.

Changed

  • Composite standardization reuses an owned observation buffer. Complete screening and composite summaries avoid copying their entire score vector.
  • The orchestration benchmark covers constant and nearly constant components, scale and offset transformations, and all-missing scores. Its checks validate calibration, coverage, and sampled weighted reductions outside measurement.

[1.7.21] - 2026-09-29

Changed

  • Small CLI text previews select top respondents in bounded batches instead of sorting every respondent. Screening, composite, and response-time summaries share the selector; summary-only previews skip rank selection entirely.
  • Equal text ranking scores consistently retain original respondent order, including ties at the preview boundary. Score and flag decisions are unchanged.
  • The CLI output benchmark includes text previews, configurable preview sizes, compression, and checks against a complete stable ordering.

[1.7.20] - 2026-09-29

Changed

  • Complete person-total correlations reuse the common item-mean profile's centering and norm within each bounded batch, reducing repeated reductions and temporary allocation. Missing pairs retain their respondent-specific calculations, and exceptional scales retain their stable fallback.
  • The person-total benchmark supports array layouts, strict missing propagation, and entirely missing inputs, with sampled direct correlation checks.

[1.7.19] - 2026-09-29

Fixed

  • CLI text, CSV, and JSON result files now use atomic replacement, including gzip, bzip2, and XZ output. Failed serialization, compression finalization, and handled interruptions preserve previous results and remove staged files.

Changed

  • Text and NPZ writers share the file replacement boundary, retaining existing permission bits and symbolic links. Compression follows the requested output suffix, and standard output, pipes, and device paths continue streaming directly.

[1.7.18] - 2026-09-29

Fixed

  • Carelessness-onset detection preserves differences between adjacent large integer responses by shifting before conversion to floating point. Signed and unsigned 64-bit response scales retain their detected positions.

Changed

  • Wide onset windows use cumulative moments for integer-valued responses when the complete calculation fits within exact floating-point integer arithmetic. This avoids a separate deviation pass for each position in the window while preserving constant-window variability. Other inputs retain the stable direct calculation, and integer matrices skip missing-value and infinity scans.
  • The onset benchmark covers integer dtypes and offsets, continuous responses, and translation-invariance checks for sampled integer respondents.

[1.7.17] - 2026-09-29

Changed

  • Wide Guttman scoring with many distinct responses counts increasing pairs across sorted runs in bounded respondent batches, avoiding quadratic pair scans. Ties, missing-response policies, and large integer values retain their exact counts; narrower matrices and small categorical scales keep their existing counters.
  • The Guttman benchmark now covers continuous responses, column-contiguous arrays, and entirely missing data, with direct pair-count correctness checks.

[1.7.16] - 2026-09-28

Fixed

  • Guttman category discovery and comparisons preserve large floating-point responses across NumPy versions, preventing duplicated response matches and normalized scores above one.

Changed

  • Response-style and paired-item scoring share scale-bound resolution while preserving integer endpoints. Combined response-pattern summaries reuse observed-response counts in bounded batches instead of scanning the matrix separately for each proportion.
  • The row-reduction benchmark supports column-contiguous input layouts.
  • Shared row correlations now bound their centering and missing-value workspaces. Four-item even–odd factors use the relative order of each pair directly, avoiding unnecessary centering and variance reductions.
  • Explicit semantic and MAD item-pair definitions share structural and integer validation. The even–odd benchmark covers array layouts and validates score availability for missing responses.
  • Individual reliability now reuses the stable row-correlation kernel and owned selection buffers, with missing-value detection shared across splits in each respondent block. Its benchmark covers array layouts and unavailable scores.
  • Psychometric synonym and antonym scoring share one bounded respondent path, checking only selected items and reusing owned correlation buffers. Shared summary statistics filter missing scores once across all reductions.
  • Split IRV reduces equally sized sections together in bounded respondent blocks instead of retaining all section score vectors. Row standard deviations no longer allocate an unused full-length mean vector. The row-reduction benchmark supports split IRV and verifies score availability, including all-missing data.
  • Onset detection shares per-block missing-response and eligibility checks, avoiding a separate full-input scan. Its rolling batches account for multiple workspaces, and its benchmark covers array layouts and unavailable detections.
  • Chi-square quantiles use direct special cases for one or two degrees of freedom, including a NumPy array path for two-degree Q-Q quantiles. Array inputs are validated once, and the Mahalanobis benchmark now includes Q-Q data.
  • Shared row means and deviations reuse their reductions under both missing-value policies, with complete blocks avoiding masked sums and blocks with mostly unavailable means skipping their centering work. The row-reduction benchmark now supports single-precision inputs and strict missing-value propagation.
  • Row medians use bounded sorting and partition buffers instead of masked-array reductions. Response-time mixtures reuse selected medians for log transformation, and their benchmark can isolate median scoring, cover array layouts, and verify unavailable scores, including single-item and entirely missing timing data.
  • Item means skip masked reductions for complete blocks, and item correlations normalize cross-products directly. Exceptional mean and correlation calculations share bounded column scaling; the psychometric benchmark now isolates item correlations and checks their availability.
  • Row-reduction and reliability benchmarks cover constant decimal responses and nearly constant profiles, including their expected zero-variance behavior. Reliability excludes constant respondent profiles once per block instead of repeating their reductions for every split.
  • Response-time mixture fitting stores component workspaces in contiguous columns and retains only the returned probability vector. Its benchmark covers raw timing units, large scales, and constant or nearly constant samples.
  • Balanced acquiescence selects paired responses together in bounded blocks. Semantic consistency batches response deviations with pair scoring, reducing temporary allocations. The pair benchmark covers array layouts, shifted and scaled responses, and entirely missing data.
  • Integer response-style proportions skip missing-value scans, and zero-tolerance midpoint scoring uses one equality comparison. Ordinary integer row means use exact native totals when their range permits. The row-reduction benchmark now covers signed and unsigned integer inputs with configurable offsets.
  • Predefined pair reductions use native integer differences for ordinary categories and release each block's selections before allocating the next. The pair benchmark also supports signed and unsigned integer offsets.
  • Even–odd scoring retains integer responses instead of converting the complete matrix to float before selecting factor items.
  • Item means use native integer totals when safe and bounded exact totals for exceptional values. Guttman recognizes contiguous integer categories directly, and strict person-total missing checks stay within respondent batches. The psychometric benchmark covers integer responses and large offsets.

Fixed

  • Inferring response-style scale bounds from entirely missing data now returns unavailable scores without warnings. Constant-scale acquiescence preserves missing respondent means and incomplete-pair policies instead of assigning every respondent a score of 0.5.
  • Extreme and midpoint scoring reject inverted scale bounds; midpoint tolerance must be finite and nonnegative. Midpoint calculation avoids overflowing when adding large integer or floating-point scale endpoints.
  • Row correlations no longer lose finite correlations when norm products overflow or underflow. Exceptional rows are rescaled before centering, single-precision inputs use double-precision accumulation, and infinite observations remain unavailable unless excluded by a missing pair.
  • Semantic consistency rejects fractional and Boolean item indices instead of silently coercing them. MAD rejects malformed pairs instead of ignoring extra entries or failing with an indexing error.
  • Individual reliability preserves finite correlations at very large or small response scales and uses double-precision accumulation for single-precision inputs. Undefined Spearman–Brown corrections return NaN rather than infinity, preventing downstream screening and composite failures. Positive NumPy integer split counts are accepted, and the documented score range now reflects the correction's possible values below -1.
  • Undefined item correlations no longer become psychometric synonym or antonym pairs at a zero cutoff. A single selected pair leaves respondent scores NaN while retaining its diagnostic count, so screening and composites preserve score availability. Cutoffs reject nonfinite and nonnumeric values.
  • Empty and entirely missing score summaries return NaN statistics without reduction warnings or errors, including psychometric and Markov summaries.
  • IRV rejects fractional and Boolean split counts and split points with clear validation errors. NumPy integer values remain supported. Counts exceeding the item count no longer allocate empty sections, even for extremely large counts. Docstring examples now show population standard deviations and split means.
  • Onset detection rejects fractional, Boolean, and nonfinite size parameters; unsigned NumPy sizes are normalized before slicing. Infinite responses leave their rows unavailable. Very large finite responses use bounded rescaling and logarithmic comparisons without changing the absolute variance floor, and centering changepoint series avoids false detections from a constant baseline.
  • Very small chi-square lower-tail probabilities no longer stop at an arbitrary probability or normal-float floor. Logarithmic inversion fixes incorrect values and nonconvergence, and representable subnormal quantiles are retained. Scalar and array helpers consistently reject invalid degree counts, including empty array calls, and gamma helpers reject NaN arguments before iteration.
  • Row means and population deviations rescale exceptional finite values to avoid overflowing means and overflowing or underflowing variances. Strict reductions now use double precision for single-precision input, fixing inaccurate means and deviations. This applies to IRV, response-pattern summaries, and timing summaries; split IRV also avoids overflowing its mean of section deviations.
  • Finite row medians no longer overflow when averaging large central values. Single-precision central pairs are averaged in double precision, 64-bit integer pairs retain exact arithmetic before final rounding, and subnormal medians are preserved. Response-time mixtures reject noninteger component counts with a clear validation error while continuing to accept NumPy integer scalars.
  • Item means no longer overflow for large finite values, and strict means promote single-precision inputs before accumulation. Person-total profiles and Guttman item ordering retain those finite means. Item correlations preserve extreme scales and nearly constant variation; constant decimal-valued items no longer acquire fabricated correlations or discovery pairs. Nonfinite observations continue to invalidate their items, including on the rescaled path.
  • Constant decimal-valued respondent profiles have exactly zero variability and retain each correlation index's zero-variance policy. Nearly constant rows use shifted, power-of-two-scaled deviations to preserve their representable variation. Rows with exactly two observed pairs correlate by direction alone, avoiding rounded near-perfect correlations and unstable reliability corrections.
  • Response-time mixtures preserve finite probabilities for extreme positive timings, including raw-time fits without a log transformation. Power-of-two scaling and standard-deviation parameters preserve the original variance floor; exceptional Gaussian tails retain their relative densities in high precision. Constant medians return equal component probabilities, initialization respects small timing spreads, and abandoned components cannot be selected as fast.
  • Acquiescence preserves normalized means across overflowing finite scale widths, subnormal ranges, and closely spaced large integer responses. Balanced scoring avoids overflowing pair sums. MAD and semantic consistency center exceptional reverse-scoring bounds and rescale overflowing pair differences; semantic normalization can retain finite ratios even when the raw mean exceeds float range.
  • Midpoint scoring preserves adjacent large integer categories and subnormal constant scales. Endpoint and midpoint comparisons no longer round explicit bounds to a lower-precision response dtype or match out-of-range integer bounds. Combined response-pattern summaries share these comparisons.
  • Integer row means preserve cancellation through exact totals, and row deviations shift before floating-point conversion to retain small differences on large baselines. Large integer pair differences and row correlations likewise preserve their observed variation, fixing misleading MAD, semantic consistency, even–odd, and split-half reliability scores.
  • Item discovery no longer loses correlations between adjacent large integer responses. Person-total scoring centers exact item totals before conversion, preserving differences between nearly identical item means. Guttman keeps integer categories and exact item rankings, including widely separated scales with closely spaced item difficulties.

[1.7.15] - 2026-09-28

Changed

  • Even–odd factor definitions now reject non-integer and non-positive sizes, while accepting NumPy integer scalars. Factors too short to provide two item pairs are skipped before correlation work.

Fixed

  • Even–odd consistency now returns NaN for respondents without any valid factor correlation instead of a numeric zero. Screening consequently counts those scores as unavailable and does not flag them as low consistency.

[1.7.14] - 2026-09-27

Changed

  • Person-fit discrimination estimation now reduces binary items together in bounded respondent batches instead of sorting responses and fitting a separate correlation matrix for each item. Centered reductions preserve item-specific missing-response handling, constant-item fallbacks, and discrimination limits.
  • The person-fit benchmark can measure discrimination estimation separately from preprocessing and complete person-fit scoring.

[1.7.13] - 2026-09-27

Changed

  • Psychometric synonym, antonym, and cutoff discovery now share a bounded item-correlation reduction instead of copying the full response matrix. Items with unavailable means are excluded from matrix multiplication while retaining the existing propagation of missing responses by item.
  • The psychometric synonym benchmark now covers item discovery, input layouts, independent or correlated responses, and concentrated or scattered missing values, with runtime measured separately from allocation tracing.

Fixed

  • Undefined item correlations no longer emit division warnings for constant responses. Single-respondent discovery returns unavailable correlations with the correct item-matrix shape instead of failing on a scalar correlation.

[1.7.12] - 2026-09-27

Changed

  • Mahalanobis scoring now uses a symmetric covariance decomposition and shares complete-row preparation across mean, covariance, and distance calculations.
  • Complete-case selection now uses a bounded missing-value scan and filters only the active row block, avoiding a full selected-response copy while preserving missing-row positions in distances and flags.
  • The Mahalanobis benchmark now covers missing-row handling and array layouts, measuring runtime separately from allocation tracing.

Fixed

  • Mahalanobis covariance conditioning no longer forms a singular-value ratio that can overflow for data with widely different feature scales.
  • A single complete observation now raises a clear validation error instead of dividing by zero when calculating sample covariance.

[1.7.11] - 2026-09-27

Changed

  • Person-fit preprocessing now reuses binary inputs without modifying them, detects binary categories with bounded scans, and converts polytomous responses in bounded blocks instead of allocating full-matrix conversion workspaces.
  • Guttman, person-total, and person-fit difficulty estimation now share one bounded item-mean reduction. Person-fit discrimination estimation also uses bounded row totals to avoid full-matrix NaN replacement copies.
  • The person-fit benchmark now accepts a category count for comparing binary and polytomous preprocessing.

Fixed

  • Person-fit difficulty estimation leaves entirely missing items unavailable without emitting a mean-of-empty-slice warning. Missing-aware scoring omits those items and returns unavailable scores for entirely missing respondents.

[1.7.10] - 2026-09-27

Changed

  • Lz person-fit scoring now uses the same bounded ability-estimation and likelihood kernels for complete and missing responses. Missing items are excluded from each respondent's reductions without switching the whole matrix to per-respondent Python loops.
  • Consolidated person-fit tests around independent scalar references and extended the benchmark to missing responses, array layouts, and both IRT models, with timing measured separately from allocation tracing.

Fixed

  • Lz scores with unavailable item parameters now remain unavailable for complete response rows, matching missing-response scoring, instead of becoming zero when the likelihood variance is NaN.

[1.7.9] - 2026-09-27

Changed

  • Missing-response and attention-check scoring now share ordered item-selection validation and use bounded respondent batches. Missing-response scoring avoids full selected-response and applicability copies; attention checks compare selected items together while preserving all four missing-response policies.
  • Added a response-check benchmark with configurable item selection, missingness, and array layout, measuring runtime separately from peak allocation.

[1.7.8] - 2026-09-27

Changed

  • Markov scoring now shares bounded sequence preparation with longstring indices and encodes each response block once. Category discovery and transition counting reuse that encoding, avoiding repeated full-matrix scans and reducing temporary allocation while preserving scores and missing-value policies.
  • The Markov benchmark now supports missing responses and measures timing separately from allocation tracing.

Fixed

  • Markov scoring preserves distinct large integer category labels instead of merging neighboring values when converting them to floating point.

[1.7.7] - 2026-09-27

Changed

  • Longstring and repeating-pattern scoring now share bounded sequence preparation and cumulative run-length kernels, replacing per-column loops and separate scalar fallbacks while preserving scores and missing-value policies.
  • Complete-response pattern workspaces now use the same bounded row budget as missing-response inputs. The sequence benchmark supports selecting individual operations for small, large, and wide matrices.

[1.7.6] - 2026-09-27

Changed

  • Missing-response longstring, repeating-pattern, and Markov scoring now use bounded NumPy batches, preserving response order and each row's observed length. Screening and composite workflows benefit automatically, with no new dependency or change to missing-value policies.
  • Added a sequence-scoring benchmark covering individual indices and complete screening/composite workflows, with configurable missingness and separate timing and allocation measurements.

Fixed

  • Short missing-response longstring inputs compare adjacent categories directly, avoiding subtraction overflow and invalid arithmetic for infinite categories.

[1.7.5] - 2026-09-21

Added

  • The screen command now exposes balanced acquiescence scoring through --acquiescence-positive-items and --acquiescence-negative-items. Ordered, 0-based polarity pairs use the existing response-scale bounds and work with every established input and output transport without adding a dependency.

Changed

  • The predefined pair benchmark now covers balanced acquiescence alongside MAD and semantic consistency. On 100,000 respondents, 80 items, 40 pairs, and 10% missing responses, balanced acquiescence takes 18.0 ms and MAD takes 18.3 ms; both retain a 6.8 MiB peak traced allocation after shared validation.

Fixed

  • Balanced acquiescence now averages raw agreement responses without reversing negatively worded items. All-agree and all-disagree patterns score 1 and 0; opposing responses across item polarities score 0.5. This corrects the previous balanced-mode calculation for both Python and the new CLI options.
  • Balanced acquiescence and MAD now reject unequal positive/negative item lists instead of silently ignoring trailing configuration. Both public paths share integer and matrix-bound validation, and explicitly empty command-line lists produce structured errors.

[1.7.4] - 2026-09-14

Added

  • All matrix-scoring commands accept --skip-rows N to discard survey-export preambles before delimiter and header detection. The option counts physical input lines and works consistently for plain, gzip, bzip2, XZ, and forward-only standard-input streams without adding a dependency.

Changed

  • Preamble removal now occurs before the existing bounded delimiter sample, so metadata punctuation cannot distort format detection and the first retained row keeps the established header, missing-value, and UTF-8 marker behavior.
  • On the reproducible 100,000-respondent, 20-item input benchmark, the unchanged default path takes 1.0340 seconds and 16.214 MiB peak traced allocation; discarding two preamble lines takes 1.0393 seconds and 16.213 MiB, a 0.5% opt-in difference with no added peak allocation.

[1.7.3] - 2026-09-07

Added

  • Command-line delimited input and text, JSON, and CSV output now support bzip2 (.bz2) and XZ (.xz) files alongside gzip. Codec selection remains suffix-based, forward-only, and entirely within the Python standard library.

Changed

  • Plain and compressed command-line files now share one UTF-8 text-stream boundary. Compressed .npy inputs are rejected consistently for all three codecs because they cannot retain the existing memory-mapped load path.
  • XZ output uses its fast low-memory preset. On the reproducible synthetic 100,000-row, five-index JSON benchmark, plain output takes 0.694 seconds and 13.5 MiB; gzip takes 2.764 seconds and 1.4 MiB, bzip2 takes 1.320 seconds and 2.2 MiB, and XZ takes 0.810 seconds and 0.6 MiB with 9.1 MiB peak traced allocation.

[1.7.2] - 2026-09-02

Fixed

  • Delimited input now validates every data row against a detected or declared header's width, reporting the first mismatched row instead of silently using that row to define the matrix width.
  • Mahalanobis distance calculation now uses its pseudo-inverse path when a nonzero covariance matrix contains an exact zero singular value, avoiding a divide-by-zero warning.

[1.7.1] - 2026-08-24

Fixed

  • Delimited input removes one leading UTF-8 byte-order mark before delimiter and header detection. Plain files, gzip-compressed files, and standard input now accept BOM-prefixed headers and headerless numeric rows consistently, while a misplaced marker still produces a structured numeric parse error. The check runs once in the existing sample buffer and adds no dependency.
  • On a 2,000,000-cell delimited benchmark, plain input parses in 187 ms and BOM-prefixed input in 189 ms, a 0.9% difference with no per-row work.

Security

  • The locked quality environment now uses pip 26.2.1, resolving PYSEC-2026-3721 without changing the package's runtime dependencies.

[1.7.0] - 2026-08-18

Added

  • Consolidated the unreleased screening, composite-scoring, response-time, archive, missing-value, and command-line improvements into the next PyPI minor release after 1.6.2.

Changed

  • Improved throughput and bounded-memory behavior across the major scoring kernels while retaining the established public API defaults.
  • Restored the public distribution version line to follow the latest version available from PyPI.

[2.19.31] - 2026-08-17

Added

  • All matrix-scoring commands accept repeatable --missing-value TOKEN options for exact, whitespace-trimmed survey-export markers such as NA, ., or -99. Tokens map to NaN only in scored numeric cells and participate in automatic header detection, retaining a headerless first row that begins with a missing marker. Plain, gzip-compressed, and standard-input streams share the implementation without adding a dependency.
  • On a 2,000,000-cell delimited benchmark, the unchanged default path parses in 210 ms while explicit token matching takes 239 ms, a 13.5% opt-in cost without an extra matrix allocation.

[2.19.30] - 2026-08-10

Added

  • All matrix-scoring commands accept --header auto|present|absent. The default preserves automatic detection, present supports fully numeric column names, and absent makes malformed first data rows fail instead of silently treating them as headers. The explicit contract works for plain, gzip-compressed, and standard-input delimited data without adding a dependency.

[2.19.29] - 2026-08-03

Changed

  • Every public and command-line NPZ writer now stages a complete archive beside its destination and replaces the target atomically. Write failures retain existing content, clean up partial temporary output, and preserve an existing file's permission bits on successful replacement. Final symbolic links remain in place while their targets receive the completed archive.
  • On a 500,000-respondent, 15-index archive, validated atomic saving takes 16.6 ms versus 12.5 ms for direct in-place serialization, with the same 4.0 MiB peak traced allocation. The response-time equivalent takes 1.6 ms versus 1.1 ms and 4.0 versus 3.9 MiB, without adding a dependency.

[2.19.28] - 2026-08-03

Added

  • save_response_time_archive() writes prepared response-time scores, Boolean flags, resolved cutoff metadata, and optional respondent identifiers as the same versioned, pickle-free NPZ schema produced by the CLI.

Changed

  • Public and command-line response-time persistence now share one validation and streaming boundary. It rejects unsupported metric/direction pairs, non-finite cutoffs, malformed or inconsistent flags, invalid score vectors, and invalid identifiers before opening the destination.
  • On a 500,000-respondent archive, validated response-time saving takes 1.4 ms versus 1.2 ms for direct unvalidated serialization, with the same 3.9 MiB peak traced allocation and no added dependency.

[2.19.27] - 2026-08-03

Added

  • load_response_time_archive() restores CLI response-time NPZ scores, flags, cutoff metadata, and optional respondent identifiers for later sensitivity analysis with response_time_score_flags().
  • Public ResponseTimeArchive and ResponseTimeMetric types document the validated result and supported persisted metrics.

Changed

  • Response-time loading always disables pickling and rejects unsupported or incomplete schemas, unsafe arrays, invalid metric/direction pairs, non-finite cutoffs, misaligned vectors, invalid identifiers, and flags inconsistent with either the fixed or percentile cutoff contract.
  • On a 500,000-respondent archive, validated response-time loading takes 1.6 ms versus 0.9 ms for direct unvalidated member access, with 5.4 MiB peak traced allocation and no added dependency.

[2.19.26] - 2026-08-03

Added

  • response_time_score_flags() reapplies fixed or percentile cutoffs to a retained direct timing score vector or fast-component mixture probabilities. Low-tail defaults suit timing summaries and consistency; high-tail defaults suit mixture probabilities.
  • The public ResponseTimeFlagDirection type documents the supported suspicious tails, and the response-time benchmark now measures repeated cutoff scenarios.

Changed

  • Single reusable score vectors and registered-index score mappings share one numeric, dimensionality, emptiness, and finiteness validator. The established response_time_flag() path delegates to the reusable implementation while preserving inclusive fixed cutoffs and tie-exclusive percentile cutoffs.
  • On a 100,000-respondent, 80-item benchmark, five cutoff scenarios fall from 1.1683 seconds and 10.0 MiB peak temporary allocation to 3.2 ms and 1.2 MiB, a 368.9x speedup without adding a dependency.

[2.19.25] - 2026-08-03

Added

  • save_score_archive() writes ordered raw registered-index vectors, optional respondent IDs, and soft failures as a compact versioned NPZ archive. Screen and composite archives round-trip through load_score_archive() and feed directly into the reusable scoring APIs.

Changed

  • Public and command-line persistence now share one streaming, uncompressed, pickle-free serializer. The public writer validates paths, registry support, vector alignment, finiteness, identifiers, and failure metadata before opening the destination, without constructing an intermediate score matrix.
  • On a 500,000-respondent, 15-index archive, validated saving takes 15.2 ms versus 14.7 ms for direct unvalidated serialization, with the same 4.0 MiB peak traced allocation and no added dependency.

[2.19.24] - 2026-08-03

Added

  • load_score_archive() restores ordered raw registered-index vectors, respondent IDs, and soft failures from versioned screen NPZ output and composite NPZ output written with --include-components. Loaded vectors feed directly into screen_scores() or composite_scores().
  • The public ScoreArchive type documents the validated loader result, and a reproducible benchmark compares validated archive loading with direct NumPy member access.

Changed

  • Score archive loading always disables pickling and rejects unsupported schema versions, incompatible result types, missing or undeclared score members, invalid registry names, unsafe object arrays, non-finite scores, misaligned vectors, duplicate metadata, and invalid respondent identifiers.
  • On a 500,000-respondent, 15-index archive, validated loading takes 11.4 ms versus 9.9 ms for direct unvalidated access, with the same 57.9 MiB peak traced allocation and no added dependency.

[2.19.23] - 2026-08-03

Added

  • composite_scores() combines retained raw component vectors with automatic direction correction, optional standardization and weights, mean/sum/max reductions, and minimum-coverage masking. This enables composite sensitivity analysis without recalculating any index.
  • A reproducible benchmark compares repeated full composite calculations with reusable component scores and verifies equivalent outputs for every scenario.

Changed

  • Screening and composite reuse now share one validated score-vector boundary. Composite direction multipliers and weights are applied one vector at a time, avoiding a retained mapping of direction-corrected copies in regular detailed runs. Composite APIs now reject non-Boolean standardization controls.
  • On a 10,000-respondent, 80-item benchmark, five weight scenarios fall from 39.7 to 1.4 ms and 12.9 to 0.7 MiB peak temporary allocation, a 27.9x speedup without adding a dependency.

[2.19.22] - 2026-08-03

Added

  • screen_scores() applies registered-index flagging, coverage, consensus, and summaries to retained score vectors, enabling fixed-cutoff, percentile, and consensus sensitivity analysis without recalculating indices. Compatible float64 arrays are retained by reference and never mutated.
  • The screening benchmark now compares repeated full calculations with the new reusable-score path and verifies identical thresholds and consensus decisions.

Changed

  • Raw-data and precomputed-score screening share one result builder, keeping direction, tie, presence, completeness, and summary behavior aligned. On a 10,000-respondent, 80-item benchmark, five percentile scenarios fall from 175.7 to 5.1 ms and 21.5 to 1.6 MiB peak temporary allocation, a 34.2x speedup without adding a dependency.

[2.19.21] - 2026-08-03

Added

  • Per-index screening summaries now report n_valid, n_unavailable, n_flagged, and flag_rate. The rate uses valid scores as its denominator and is unavailable when an index has no valid scores.
  • Text, JSON, and NPZ screening output expose the new coverage metrics. NPZ archives retain aligned valid, unavailable, and rate arrays.

Changed

  • Respondent flag counts, valid-score counts, and per-index summaries now share one non-stacking reduction pass. On a 500,000-respondent, 20-index benchmark with 10% unavailable scores, median reduction time falls from 29.90 to 27.51 ms with peak temporary allocation unchanged at about 15.0 MiB.
  • Strict type checking now covers all benchmark modules in addition to the library, preventing synthetic result fixtures from drifting from public types.

Fixed

  • The CLI output benchmark again runs for JSON, CSV, and NPZ screening results after its synthetic fixture fell behind recently added result fields.

[2.19.20] - 2026-08-03

Added

  • A reproducible shared flagging benchmark covering fixed and percentile cutoffs, missing scores, throughput, and peak temporary allocation.

Changed

  • Shared flagging now compares complete score vectors directly and derives sample cutoffs with a NaN-aware percentile reduction, avoiding repeated validity masks and filtered-score copies. On a one-million-score benchmark with 10% missing values, fixed-cutoff median time falls from 2.46 to 0.15 ms and peak temporary allocation from 9.6 to 1.0 MiB; percentile flagging falls from 10.74 to 8.55 ms and from 13.7 to 10.1 MiB, without adding a dependency.

[2.19.19] - 2026-08-03

Changed

  • The integration dependency range now excludes yanked Polars 1.43.0 and the development lock resolves Polars 1.43.2 instead. The NumPy-only base install and optional plotting requirements are unchanged.

[2.19.18] - 2026-08-03

Added

  • Screening now accepts per-index tail-percentile overrides through the percentiles mapping and repeatable --index-percentile INDEX=VALUE CLI option.
  • Screening results, text, JSON, and NPZ output now identify each cutoff as fixed, percentile, or presence and retain the requested tail percentile.

Changed

  • Fixed and percentile overrides for the same index are rejected as ambiguous. Presence-mode indices reject both numeric cutoff types, while the global percentile remains the fallback for indices without an override.

[2.19.17] - 2026-08-03

Added

  • Screening now accepts min_valid_indices / --min-valid-indices to require enough available index scores before a respondent can receive a consensus flag.
  • Screening results and every structured CLI format now report respondent-level valid_index_counts and consensus_eligible values. CSV output includes the equivalent singular columns, and configured NPZ archives retain the minimum.

Changed

  • Composite and screening completeness controls now reuse one shared validator. Availability counts continue to accumulate one score vector at a time, without allocating a respondent-by-index matrix.

[2.19.16] - 2026-08-03

Added

  • Attention-check scoring now supports explicit pass, fail, omit, and propagate missing-response policies through direct APIs, IndexOptions, and --infrequency-missing. The legacy pass policy remains the default.
  • infrequency_flag() can now score and flag failure proportions, including missing-aware denominators and unavailable-row handling.

Changed

  • Attention-check configuration now rejects non-Boolean proportion controls, duplicate or non-integer item indices, non-finite expected responses, and invalid count or proportion thresholds.

[2.19.15] - 2026-08-03

Added

  • A reproducible Mahalanobis throughput and peak-allocation benchmark with configurable matrix size, repetitions, warmup, and random seed.

Changed

  • Mahalanobis covariance and quadratic-form evaluation now use bounded respondent blocks, and all-zero covariance matrices use an exact zero pseudo-inverse. On a 100,000-respondent, 80-item benchmark, median time falls from 23.6 to 18.3 ms and peak temporary allocation from 123.1 to 7.6 MiB without adding a dependency.

[2.19.14] - 2026-08-03

Added

  • Missing-response rates now accept respondent-specific Boolean applicability masks. Non-applicable cells are excluded from the missing count and denominator; rows without applicable selected items return unavailable scores and remain unflagged.
  • Fixed required-item subsets now flow through IndexOptions, screening, composite scoring, and the --missing-item-indices command-line option.

[2.19.13] - 2026-08-03

Added

  • The carelessness-onset benchmark now accepts a configurable missing-response rate for reproducing both complete and missing-data workloads.

Changed

  • Missing-response onset detection now compresses bounded row blocks into equal retained-length groups and reuses the vectorized complete-response kernel instead of calculating each respondent independently. On a 10,000-respondent, 80-item benchmark with 10% missing responses, median time falls from 1.049 seconds to 19.9 ms. Peak temporary allocation rises from 0.3 to a bounded 2.6 MiB, without adding a dependency.

[2.19.12] - 2026-08-03

Added

  • A reproducible complete-response carelessness-onset benchmark with configurable matrix size, response scale, window, minimum length, and seed.

Changed

  • Complete-response onset detection now calculates stable sliding-window variability from rolling means and bounded deviation buffers, while its changepoint test retains candidate-position workspaces instead of complete centered and test-statistic matrices. On a 100,000-respondent, 80-item benchmark, median time falls from 187.7 to 109.6 ms and peak temporary allocation from 53.6 to 12.6 MiB, without adding a dependency.

[2.19.11] - 2026-08-03

Added

  • A reproducible split-half individual-reliability benchmark with configurable matrix size, response scale, missing rate, split count, and random seeds.

Changed

  • Individual reliability now generates its established random item splits once and scores them across bounded respondent blocks, reusing complete floating selections as centering buffers. On a 100,000-respondent, 80-item benchmark with 20 splits and 10% missing responses, median time falls from 941.9 to 579.0 ms and peak temporary allocation from 161.2 to 12.0 MiB, without adding a dependency.

[2.19.10] - 2026-08-02

Added

  • A reproducible Guttman throughput and peak-allocation benchmark covering configurable matrix sizes, categorical scales, and missing-response rates.

Changed

  • Guttman item means, difficulty-ordered selection, valid-response counts, and error accumulation now run in bounded respondent batches. On a 100,000-respondent, 80-item, five-category benchmark with 10% missing responses, median time falls from 137.0 to 112.3 ms and peak temporary allocation from 250.2 to 21.7 MiB, without adding a dependency.

Fixed

  • Large low-cardinality datasets no longer switch away from categorical accumulation solely because the respondent-by-category product is large; fractional categories and high-cardinality responses preserve the direct ordered-pair definition within the same row bound.

[2.19.9] - 2026-08-02

Added

  • Standalone semantic_syn_flag() and semantic_ant_flag() helpers now expose the registry's low-consistency flagging behavior for direct semantic-pair workflows. MAD accepts an optional fractional scale_min, also available as mad_scale_min in IndexOptions and --mad-scale-min in the CLI.

Changed

  • Predefined semantic and MAD item pairs now share a bounded absolute-difference reducer. On a 100,000-respondent, 80-item, 40-pair benchmark with 10% missing responses, semantic synonym scoring falls from 56.6 to 44.0 ms and 173.3 to 6.8 MiB, semantic antonym scoring from 68.1 to 46.4 ms and 228.2 to 6.8 MiB, and MAD from 31.8 to 16.8 ms and 161.0 to 6.8 MiB, without adding a dependency.

Fixed

  • MAD no longer truncates fractional response-scale endpoints during reverse scoring, and its documentation now correctly describes high scores as paired inconsistency. Entirely missing semantic and MAD matrices return unavailable scores without emitting empty-slice warnings.

[2.19.8] - 2026-08-02

Changed

  • Response-time mean, median, standard-deviation, consistency, and mixture preprocessing now use bounded row reductions instead of complete timing-matrix workspaces. On a 100,000-respondent, 80-item benchmark with 10% missing times, peak temporary allocation falls from 77.1 to 1.3 MiB for the mean, 206.8 to 9.6 MiB for the median, 77.1 to 4.0 MiB for the standard deviation, 77.9 to 4.0 MiB for consistency, and 206.8 to 9.6 MiB for mixture scoring. Consistency time also falls from 40.7 to 27.3 ms, without adding a dependency.

Fixed

  • Entirely missing response-time rows now return unavailable mean, median, standard-deviation, minimum, and consistency scores without emitting NumPy empty-slice warnings.

[2.19.7] - 2026-08-02

Changed

  • IRV, simple and balanced acquiescence, extreme and midpoint response proportions, and combined response-pattern summaries now reduce in bounded row batches through a shared mean and population-standard-deviation layer. On a 100,000-respondent, 80-item benchmark with 10% missing responses, peak temporary allocation falls from 77.1 to 4.0 MiB for IRV, 138.2 to 2.3 MiB for acquiescence, 39.7 to 1.6 MiB for U3, 122.1 to 1.6 MiB for midpoint responding, and 118.3 to 4.0 MiB for combined response patterns. Combined response-pattern time also falls from 50.8 to 39.7 ms, without adding a dependency.

Fixed

  • Missing-aware row means and standard deviations now return unavailable scores for entirely missing rows without emitting NumPy empty-slice warnings.

[2.19.6] - 2026-08-02

Changed

  • Person–total item-profile means and respondent correlations now run in bounded batches through the shared row-correlation kernel instead of materializing several complete respondent-by-item arrays. On the 100,000-respondent, 80-item benchmark, median time falls from 56.7 to 24.0 ms and peak temporary allocation from 322.7 to 4.9 MiB, without adding a dependency.

Fixed

  • Person–total scoring now ignores entirely unavailable items without emitting a warning when missing removal is enabled. Disabling missing removal restores strict propagation instead of silently discarding contaminated items, while constant respondent and item profiles remain unavailable.
  • The index catalog now describes person_total as agreement with the sample item profile rather than as an extreme total-score measure.

[2.19.5] - 2026-08-02

Changed

  • Psychometric synonym and antonym scoring now shares the allocation-light row-correlation kernel with even–odd consistency and evaluates every missing-response and seeded-resampling path in bounded respondent batches. On the 8,000-respondent, 40-item, 741-pair benchmark, median time falls from 32.2 to 15.6 ms and peak temporary allocation from 192.4 to 8.2 MiB, without adding a dependency.

Fixed

  • Zero correlation thresholds now select only real item pairs from the strict lower triangle instead of also admitting diagonal and mirrored entries whose triangular-fill value happened to be zero.

[2.19.4] - 2026-08-02

Changed

  • Multi-factor even–odd consistency now accumulates respondent sums and valid counts as each factor is scored instead of retaining and stacking every correlation vector. Its row-correlation kernel also avoids masked data copies and elementwise product temporaries, while compatible floating NumPy inputs are reused during validation. On the 100,000-respondent, 80-item, 20-factor benchmark, median time falls from 138.8 to 84.9 ms and peak temporary allocation from 112.2 to 7.8 MiB, without adding a dependency.

[2.19.3] - 2026-08-02

Changed

  • Markov transition entropy now counts categorical pairs in bounded row batches and evaluates the equivalent conditional-entropy count formula, eliminating a transition-sized respondent ID vector and several probability tensors. On the 100,000-respondent, 80-item, five-state benchmark, median time falls from 99.2 to 75.0 ms and peak temporary allocation from 268.6 to 85.5 MiB, without adding a dependency.

Fixed

  • Markov inputs with more than 64 distinct states now count only states and transition pairs observed within each row instead of attempting a dense global state-square allocation. High-cardinality and infinite categorical values therefore remain bounded and produce finite scores.

[2.19.2] - 2026-08-02

Changed

  • Response-time Gaussian-mixture fitting now reuses its responsibility and scratch workspaces and evaluates ordinary densities in place. On the 100,000-observation, two-component EM benchmark, median time falls from 35.4 to 33.5 ms and peak temporary allocation from 6.1 to 4.7 MiB. The superseded internal normal-density helper is removed, without adding a dependency.

Fixed

  • Mixture posterior rows whose ordinary Gaussian densities all underflow are now normalized in log space instead of returning an all-zero probability vector. Non-finite respondent medians are also excluded from fitting and returned as unavailable results.

[2.19.1] - 2026-08-02

Changed

  • Composite and lz scoring now share one overflow-safe NumPy logistic kernel, removing four duplicate or direct exponential calculations. On the 10,000-respondent, 80-item lz benchmark, median time falls from 144.7 to 132.8 ms while peak temporary allocation remains 13.8 MiB, without adding a dependency.

Fixed

  • Complete and missing-data lz likelihood calculations no longer emit overflow warnings for extreme user-supplied difficulty, discrimination, or theta values; probabilities retain finite-tail precision before established clipping is applied.

[2.19.0] - 2026-08-02

Added

  • ier composite --include-probability now exports the overflow-safe, uncalibrated logistic transform alongside composite scores in text, JSON, CSV, and NPZ. Scoring still runs once, flags remain defined in composite-score units, structured formats identify the probability scale, and default schemas are unchanged. At 100,000 respondents and five component indices, probability-enabled CSV used 0.274 MiB peak output allocation, JSON used 0.536 MiB, and NPZ used 0.900 MiB, without adding a dependency.

[2.18.0] - 2026-08-02

Added

  • composite_probability() now accepts return_diagnostics=True, matching the other composite helpers and returning ordered per-index soft-failure messages alongside the unchanged probability vector. The default remains a NumPy array, and the typed overloads preserve precise return types.

Fixed

  • Logistic composite transformation now uses an overflow-safe piecewise calculation that preserves finite-tail precision and maps infinite scores to exact endpoints without warnings. On 500,000 scores spanning -1,000 to 1,000, the transform used 8.1 MiB peak temporary allocation and 1.9 ms median time, without adding a dependency.

[2.17.0] - 2026-08-02

Added

  • ier composite now accepts mutually exclusive --threshold and --percentile options for opt-in respondent flagging. Fixed cutoffs include equality, percentile cutoffs use strict upper-tail comparisons, and the already-computed composite score vector is reused without rescoring indices. Text, JSON, CSV, and NPZ expose the flags; structured metadata records the resolved cutoff and its source, while default aggregate-only schemas remain unchanged. At 100,000 respondents and five component indices, flagged CSV used 0.274 MiB peak temporary allocation, JSON used 0.542 MiB, and NPZ used 0.900 MiB, without adding a dependency.

[2.16.0] - 2026-08-02

Added

  • ier composite now supports --standardize and --no-standardize, matching the Python composite APIs while retaining standardized scoring as the default. Text, JSON, and NPZ outputs record the effective setting so aggregate values remain interpretable after export; CSV continues to contain only respondent rows. On 500,000 respondents and 20 indices, the raw-score mean used 8.6 MiB peak temporary allocation and 13.8 ms median reduction time, compared with 17.2 MiB and 52.9 ms for standardization, without adding a dependency.

[2.15.0] - 2026-08-02

Added

  • ier composite --include-components now exposes successful raw per-index scores and respondent-level valid-index counts in text, JSON, CSV, and NPZ. The option reuses the detailed summary workflow without rescoring, retains existing failure metadata, streams JSON arrays and CSV rows, and writes typed NPZ members without stacking a component matrix. On 100,000 respondents and five indices, detailed CSV used 0.274 MiB peak temporary allocation, JSON used 0.525 MiB, and NPZ used 0.899 MiB, without adding a dependency.

Fixed

  • Composite text ranking now excludes NaN and infinite aggregate scores instead of allowing unavailable rows to appear above finite scores.

[2.14.1] - 2026-08-02

Fixed

  • ier composite now preserves the soft per-index failures already collected during scoring instead of silently discarding them, and ier screen mirrors its existing failures to standard error as well. Every output format emits a concise warning, text output includes an errors section, JSON includes an errors object, and NPZ stores aligned error_names and error_messages vectors. CSV remains an unchanged respondent table, score computation still runs once, and no dependency was added.

[2.14.0] - 2026-08-02

Added

  • All composite APIs now accept an optional min_valid_indices requirement. Respondents with fewer available component scores receive NaN before flagging or logistic transformation, while the default preserves established reduction behavior. Detailed summaries expose respondent-aligned valid_index_counts, and the CLI accepts --min-valid-indices and records the rule in text, JSON, and NPZ metadata. Equal-weight means reuse their existing count vector: on 500,000 respondents and 20 indices, enabling a 10-index minimum held peak temporary allocation at 17.2 MiB and changed median time from 48.3 to 48.4 ms. The weighted path adds about 4.2 ms and one 3.8 MiB integer count vector only when the rule is enabled, without adding a dependency.

[2.13.0] - 2026-08-02

Added

  • All composite APIs now accept optional positive finite per-index weights, applied after direction correction and optional standardization. Weighted means renormalize over the indices available for each respondent, partial mappings leave unspecified selected indices at weight 1, and resolved weights are included in composite summaries. The CLI accepts repeatable --weight INDEX=VALUE options and records explicit overrides in text, JSON, and NPZ metadata without adding a dependency. On a 500,000-respondent, 20-index standardized mean, weighting adds about 4.5 ms and 3.9 MiB of temporary allocation over the equal-weight reducer.

Changed

  • Standardizing a constant index now retains its missing values instead of converting them to zero contributions, so per-respondent composite means can correctly exclude unavailable evidence.

[2.12.2] - 2026-08-02

Changed

  • Screening flag counts and composite mean, sum, and maximum reductions now accumulate one index at a time instead of constructing additional respondent-by-index matrices. On 500,000 respondents and 20 indices, a standardized composite mean reduces peak temporary allocation from 252.3 MiB to 16.8 MiB and median reduction time from 79.9 to 42.2 ms; screening flag counts drop from 13.4 MiB to 3.9 MiB and from 5.5 to 3.7 ms, without adding a dependency.

[2.12.1] - 2026-08-02

Changed

  • CLI screening, composite, and response-time JSON output now writes respondent arrays in bounded chunks to plain files, gzip files, or standard output while preserving the existing schema and strict null handling. For 100,000 respondents and five indices, screening JSON reduces peak output allocation from 44.5 MiB to 0.6 MiB, median serialization time from 0.83 to 0.57 seconds, and output size from 20.4 to 12.8 MiB without adding a dependency.

[2.12.0] - 2026-08-02

Added

  • screen() and all composite APIs now accept an opt-in workers count, and the corresponding CLI commands accept --workers. Independent indices run concurrently through the Python standard library while results and failures retain selection order, and duplicate index selections are rejected rather than being scored ambiguously. Four workers reduce default screening time from 86.4 to 42.8 ms for 20,000 respondents × 80 items and from 531.0 to 282.5 ms for 100,000 × 80, without changing the single-worker default or dependencies.

[2.11.1] - 2026-08-02

Changed

  • CLI matrix loading, text/JSON/CSV serialization, and command coordination now live in focused internal modules. The coordinator is roughly half its prior size while input memory use, serialization throughput, output schemas, and the NumPy-only runtime dependency remain unchanged.

[2.11.0] - 2026-08-02

Added

  • CLI scoring commands now write versioned, pickle-free .npz result archives with typed scores, flags, metadata, failures, and optional respondent IDs. On a 100,000-respondent, five-index result, NPZ output is about 650 times faster than CSV and produces a 5.2 MiB file instead of 11.2 MiB, without adding a dependency.

[2.10.1] - 2026-08-02

Changed

  • CLI CSV output now writes rows directly to plain files, gzip files, or standard output instead of retaining all rows and the complete serialized document. A 100,000-respondent, five-index export reduces peak allocation from 136.6 MiB to 0.3 MiB and median runtime from 1.93 to 1.81 seconds.

[2.10.0] - 2026-08-02

Added

  • CLI scoring commands now accept uncompressed .npy matrices and memory-map them read-only for fast, low-overhead loading without new dependencies. A 25,000-respondent, 80-column benchmark initializes mapped input in about 0.3 ms with 0.3 MiB peak allocation, versus 343 ms and 16.4 MiB for CSV.

[2.9.2] - 2026-08-02

Changed

  • CLI matrix loading now validates and converts rows directly into a compact numeric buffer instead of retaining a full raw string matrix during conversion. A 25,000-respondent, 80-column benchmark reduces peak allocation from about 100 MiB to 16 MiB and runs roughly 2.4 times as fast.

[2.9.1] - 2026-08-02

Added

  • CLI scoring commands now accept - for forward-only standard input and explicit standard output. All CLI commands transparently read or write .gz files where applicable, using only the Python standard library.

[2.9.0] - 2026-08-02

Added

  • The new ier response-time command scores timing matrices with mean, median, standard-deviation, minimum, consistency, or Gaussian-mixture metrics. It supports fixed or percentile flagging, named respondent and timing columns, mixture configuration, and text, JSON, or CSV output without new dependencies.

[2.8.2] - 2026-08-02

Changed

  • Complete-data psychometric synonym and antonym scoring now evaluates respondent-by-pair workspaces in bounded batches. A 5,000-respondent, 80-item dense-pair benchmark reduces peak allocation from about 512 MiB to 10 MiB and runs roughly 1.2 times as fast.

[2.8.1] - 2026-08-02

Changed

  • Complete-matrix lz person-fit scoring now batches safeguarded theta estimation and likelihood calculations in cache-sized workspaces instead of looping over respondents. The 10,000-respondent, 80-item benchmark runs roughly six times as fast, while missing-data rows retain the scalar fallback.

[2.8.0] - 2026-08-02

Added

  • ier screen and ier composite now accept repeatable, comma-separated --item-columns selections. Named items are resolved from the input header in requested order, can be combined with --id-column, and allow survey exports containing unselected nonnumeric metadata to be scored directly.

[2.7.4] - 2026-08-02

Changed

  • Resampled individual reliability now streams per-respondent correlation sums and valid counts instead of retaining a respondent-by-split matrix, uses a faster missing-free correlation path, and isolates seeded randomness from NumPy's global state. The 10,000-respondent, 80-item, 100-split benchmark runs roughly 2.6 times as fast with lower peak workspace.

[2.7.3] - 2026-08-02

Changed

  • Complete-matrix carelessness-onset detection now evaluates sliding-window variability and changepoint statistics in bounded vectorized batches instead of nested respondent/window loops. The 10,000-respondent, 80-item benchmark runs over 200 times as fast while missing-data rows retain the established fallback behavior.

[2.7.2] - 2026-08-02

Changed

  • Public percentile-capable flag helpers now share one cutoff-boundary policy: explicit thresholds include scores exactly at the cutoff, while sample-percentile thresholds continue to flag only the strict tail. This brings direct helper behavior in line with fixed thresholds in screen().

[2.7.1] - 2026-08-02

Changed

  • Complete-matrix repeating-pattern scoring now streams match lengths in reverse using respondent-sized, smallest-safe integer vectors instead of several respondent-by-position integer matrices; the 10,000-respondent, 80-item benchmark runs roughly 1.6 times as fast with substantially less workspace, reducing the same default-screen benchmark by about 10%.

[2.7.0] - 2026-08-02

Added

  • screen() and all composite orchestration APIs accept strict=True to raise a contextual error as soon as any selected index fails.
  • The screen and composite CLI commands expose the same policy through --strict, while soft per-index errors remain the default.

[2.6.1] - 2026-08-02

Fixed

  • Percentile-based flagging APIs now consistently reject booleans, non-finite thresholds, and percentiles outside 0–100 instead of silently returning misleading flags.
  • Response-time flagging now shares the same validated threshold implementation as response-matrix indices.

[2.6.0] - 2026-08-02

Added

  • Public missing_rate() and missing_rate_flag() helpers quantify response omissions across all items or a validated item subset.
  • Opt-in missing_rate registry support for screening, composites, CLI output, fixed thresholds, and index discovery without changing default workflows.

[2.5.0] - 2026-08-02

Added

  • CLI --id-column NAME support excludes a named identifier column from scoring and preserves unique, nonblank respondent IDs in text, JSON, and CSV outputs.

[2.4.3] - 2026-08-02

Changed

  • Guttman scoring now directly encodes bounded integer response scales and counts grouped category positions in vectorized passes; the 10,000-respondent, 80-item categorical benchmark runs roughly four times as fast.

[2.4.2] - 2026-08-02

Changed

  • Markov transition scoring now directly encodes bounded integer response scales and compacts unused states without sorting the full matrix; the 10,000-respondent, 80-item index benchmark runs roughly twice as fast.

[2.4.1] - 2026-08-02

Changed

  • Mahalanobis distance evaluation now uses a BLAS-backed matrix product for the quadratic form; the 10,000-respondent, 80-item index benchmark runs roughly 31 times faster and default screening runs roughly twice as fast.

[2.4.0] - 2026-08-02

Added

  • Public index_catalog() metadata for registered indices, including flagging modes, orchestration defaults, composite availability, and required options.
  • ier indices discovery output in text, JSON, or CSV format.

Changed

  • Local and CI version checks now verify that the editable project entry in uv.lock matches project.version before dependency synchronization.

[2.3.0] - 2026-08-02

Added

  • screen() accepts fixed per-index thresholds alongside percentile defaults and returns the actual cutoff applied for each successful index.
  • The CLI accepts repeatable --threshold INDEX=VALUE options and includes applied thresholds in JSON and text output.

[2.2.6] - 2026-08-02

Changed

  • Longest-run scoring now processes complete matrices column-wise instead of looping over respondents; the 10,000-respondent, 80-item index benchmark runs roughly 47 times faster and the 1,000-respondent, 50-item default screening benchmark runs roughly 1.6 times faster.

[2.2.5] - 2026-08-02

Changed

  • Repeating-pattern scoring now evaluates complete matrices in vectorized batches; the 1,000-respondent, 50-item index benchmark runs roughly 36 times faster and default screening runs roughly three times faster.

[2.2.4] - 2026-08-02

Added

  • screen() now returns configurable respondent-level consensus_flags alongside per-index flags and counts, with a default min_flags=2 agreement threshold.
  • The CLI exposes --min-flags and includes consensus decisions in text, JSON, and CSV output.

[2.2.3] - 2026-08-02

Changed

  • Guttman scoring now counts ordered response pairs without materializing every respondent-by-item-pair value matrix, using an adaptive categorical fast path and bounded-memory fallback for high-cardinality data; the 3,000-respondent, 100-item categorical benchmark uses roughly one-sixth the peak process memory and runs about 1.7 times faster.

Fixed

  • All-missing Guttman inputs now return missing scores without emitting empty-mean runtime warnings.

[2.2.2] - 2026-08-02

Changed

  • Transition-entropy scoring now uses its vectorized batch implementation for complete matrices under the default missing-value policy; the 10,000-respondent, 80-item transition benchmark is roughly five times faster.

[2.2.1] - 2026-08-02

Changed

  • Repeating-pattern screening now precomputes constant-pattern and periodic-match runs, avoiding repeated sorting and rescanning for every candidate position; the 1,000-respondent, 80-item screen benchmark is roughly nine times faster.

Fixed

  • Architecture and index documentation no longer describes dependency-free statistical routines as requiring SciPy.

[2.2.0] - 2026-07-29

Added

  • Dependency-free normal density/quantile helpers, bounded Newton IRT theta estimation, and chi-square quantiles backed by regularized incomplete-gamma series and continued-fraction calculations.
  • SciPy-reference regression coverage for chi-square probabilities from 1e-12 through 1 - 1e-12 and 1–1,000 degrees of freedom.
  • Targeted test, integration, lint, docs, and security dependency groups.

Changed

  • Mahalanobis chi-square and z-score flagging, Mahalanobis Q-Q data, lz theta estimation, and response-time mixtures now run consistently in the base install.
  • The lz theta solver now converges to a tighter score-equation tolerance; locked lz scores can shift by roughly 1e-6 from SciPy's default bounded tolerance.
  • Ruff now owns lint and static-security checks, including selected Pylint and flake8-bandit rules; CI jobs install only their required dependency groups.
  • The legacy full extra is an empty compatibility alias.

Removed

  • SciPy and scipy-stubs, the redundant wheel build requirement, direct transitive pins for Pillow/Pygments/Requests, and unused pre-commit project dependency.
  • Redundant Bandit and non-gating Pylint jobs and dependencies.

[2.1.7] - 2026-07-28

Fixed

  • Semantic-antonym scoring now reflects reverse-keyed responses around the configured or inferred response scale, so consistent antonym pairs receive high consistency scores instead of being clipped as maximally inconsistent.

[2.1.6] - 2026-07-27

Fixed

  • longstring() now rejects numeric and multidimensional NumPy arrays instead of silently converting them into meaningless character-run results; numeric survey matrices remain supported through longstring_scores().

[2.1.5] - 2026-07-27

Fixed

  • CLI matrix loading now treats blank delimited fields as missing values instead of rejecting the file, and header detection no longer discards a first data row whose first value is blank.

[2.1.4] - 2026-07-27

Fixed

  • CLI CSV export now represents NaN and infinite scores as empty cells instead of non-numeric nan and inf strings.

[2.1.3] - 2026-07-27

Fixed

  • Invalid multi-character, empty, or newline CLI delimiters now return a concise error instead of leaking a Python traceback.

[2.1.2] - 2026-07-27

Fixed

  • CLI matrix loading now accepts the documented whitespace-delimited format, including mixed runs of spaces and tabs.

[2.1.1] - 2026-07-27

Fixed

  • CLI analysis and output failures now return concise error: messages instead of leaking Python tracebacks for invalid user input.
  • CLI JSON export now emits standards-compliant null values for NaN and infinite scores or summary statistics.

[2.1.0] - 2026-07-24

Added

  • Architecture note (docs/architecture.md) covering registry design, flagging, NA policy, and uncalibrated composite probabilities.
  • Expanded golden / R-parity fixtures for guttman, markov, person_total, midpoint, lz, and onset, plus JSON harness under tests/fixtures/parity/.
  • Synthetic detection-rate benchmark (benchmarks/bench_detection.py).
  • GitHub issue templates (bug / feature / methods) and a PR template.
  • Shared SciPy install hints via _optional_imports.require_scipy.

Changed

  • Docs homepage quick start uses IndexOptions (2.0-compatible).
  • dev extra composes full, plot, and docs instead of duplicating pins.
  • Lint and security workflows also run on pushes to main.
  • Screen throughput benchmark updated for the IndexOptions-only API.
  • LICENSE copyright years updated through 2026.
  • Package version bumped to 2.1.0.

Removed

  • Unused raise_missing_config flag on score_registered_indices() (soft-fail is the only orchestration path).

[2.0.0] - 2026-07-24

Breaking

  • screen() / composite() / composite_flag() / composite_summary() / composite_probability() accept configuration only via options=IndexOptions(...). Legacy per-index keyword arguments were removed.
  • Removed deprecated build_index_options().
  • String run-length helpers in ier.longstring are private (_run_length_encode, _run_length_decode, _longstr_message, _avgstr_message). Prefer longstring() / longstring_scores().
  • mahad(..., flag=True, method="zscore") requires SciPy (same as chi2); it no longer silently falls back to a hardcoded threshold of 2.0.

Added

  • CLI JSON/CSV export (--format json|csv, optional --output), plus IndexOptions knobs (--evenodd-factors, MAD/semantic/infrequency lists, etc.).
  • Clearer jagged-CSV error when loading CLI matrices.
  • Golden parity fixtures for longstring_pattern, mahad (iqr), psychsyn, and evenodd.
  • Public export of longstring_scores.
  • SECURITY.md and .github/CODEOWNERS.
  • Release and PyPI publish workflows run the full CI test suite before shipping.
  • CI also runs on pushes to main.

Changed

  • composite() soft-fails missing index config (e.g. evenodd without factors), matching screen(); if no index succeeds, it still raises.
  • Local scripts/check.sh pylint uses --fail-under=9.0 to match CI.
  • Version-check workflow is callable-only (no duplicate PR trigger).
  • Response-time helpers documented as intentionally out of the registry.
  • Specialized tests split into test_composite.py, test_response_time.py, and a smaller test_specialized_indices.py.
  • Package version bumped to 2.0.0.

Fixed

  • Changelog no longer claims Codecov fails the job on upload error; uploads stay non-blocking when CODECOV_TOKEN is unset.

[1.8.0] - 2026-07-23

Added

  • Public IndexOptions config object for screen() / composite() (preferred over long keyword lists); legacy kwargs remain supported when options is omitted.
  • Package __version__ and ier CLI (ier screen, ier composite).
  • Unified contributor check script (scripts/check.sh).
  • Golden IRV / longstring parity fixtures (tests/test_golden_parity.py) and a concrete R-package comparison table in the docs.
  • Docs build gated on pull requests via the veto workflow.

Changed

  • screen() / composite() share the full IndexOptions surface (including scale bounds, longstring pattern length, onset, and acquiescence settings).
  • screen() reuses shared threshold_flags for percentile flagging.
  • visualize helpers are typed against ScreenResult.
  • Version-check CI requires a bump only when src/ changes.
  • Pre-commit Ruff pin aligned to 0.15.x; docs workflow uses actions/checkout@v7.
  • Package version bumped to 1.8.0.

Fixed

  • Release workflow no longer overwrites curated CHANGELOG.md when generating GitHub release notes.

[1.7.0] - 2026-07-23

Added

  • Registry coverage for guttman, psychant, individual_reliability, onset, semantic_syn, semantic_ant, and infrequency in screen() / composite().
  • onset presence-based flagging mode in screen().
  • Expanded screen() / composite() configuration knobs for newly registered indices.
  • MkDocs documentation site (getting started, workflows, index catalog, thresholds, R notes, API reference).
  • Benchmark script for the screen() hot path (benchmarks/bench_screen.py).
  • Example scripts under examples/.
  • pandas / polars smoke tests; pandas and polars added to the dev extra.
  • Curated changelog and expanded PyPI classifiers (Python 3.11–3.14).

Changed

  • Default screen() / composite() Mahalanobis scoring uses a NumPy-safe path (method="iqr" distances). SciPy is only required for direct mahad(..., flag=True, method="chi2") (and related SciPy-only helpers).
  • score_registered_indices soft-catches ValueError, RuntimeError, and TypeError into per-index errors.
  • CI coverage gate aligned to 90%; Codecov upload remains non-blocking when a token is unset; Bandit no longer falls back to a looser severity filter.
  • Package version bumped to 1.7.0.

Fixed

  • Base-install footgun where default screening could abort on missing SciPy when computing Mahalanobis distances with the previous chi-squared default path.

[1.6.3] - 2026-05

Changed

  • Dependency and CI maintenance releases (Dependabot, lockfile, Actions bumps).

Added

  • Index orchestration registry hardening and related workflow improvements.

Earlier

Feature work through early 2026 introduced screening workflows, composite scoring, additional indices (including MAD, lz, acquiescence), visualizations, and multi-OS / multi-Python CI.