Migrating to 0.8.0

Status: the 0.8.0 candidate was never published; every step in this guide ships in 0.8.1 (decision D-0212). Read 0.8.0 below as 0.8.1; see Migrating to 0.8.1.

Input validation

Quantitative semantics, calibration, posterior schemas, MinHash controls, law discovery, and sequential-design budgets reject malformed, non-finite, or out-of-range values earlier. Callers that relied on silent acceptance must validate inputs and handle ValueError at the public boundary.

The same tightening now covers several previously-silent numerical and shape cases:

  • WishartDistribution, InverseWishartDistribution, MatrixNormalDistribution, and NormalWishart reject a non-symmetric or non-positive-definite matrix argument instead of accepting it and producing a downstream NaN or a misleading log-density.

  • The Gaussian and Student-t copulas, and LKJDistribution, reject a correlation-like matrix argument that is not symmetric, does not have a unit diagonal, or is not positive definite.

  • GaussianBelief rejects an indefinite covariance matrix; CategoricalBelief rejects a labels argument whose length does not match the probability vector.

  • Conformal prediction (weighted_conformal and the underlying quantile routine) rejects n=0 and negative calibration or test weights instead of returning a degenerate or silently-wrong interval.

  • mixle.epistemic.discrepancy.mmd rejects empty samples and a non-positive bandwidth.

Callers constructing these objects from external or previously-unvalidated data must catch ValueError at the construction boundary rather than relying on the object silently accepting an invalid input.

Self-evolution verify gate

mixle.evolve.verify.Verdict.calibration_status replaces the previously implicit pass/fail calibration check with an explicit three-state string: "passed", "failed", or "unavailable" (calibration could not be computed for this model pair, or was not requested – this does not block promotion, same as "passed"). Verdict.calibrated remains available as a derived property (calibration_status != "failed"), so existing code that reads verdict.calibrated or verdict.as_dict()["calibrated"] is unaffected; as_dict() additionally now includes calibration_status. Separately, the non-nested cross-check that challenger_beats_champion(..., nonnested=True) runs now downgrades the verdict to "tie" if the cross-check itself raises, rather than letting an unrelated earlier verdict stand unverified – code that asserted a promotion decision purely from the paired test, ignoring a raised cross-check, may see a different (safer) outcome.

Empirical law discovery

Candidate forms are selected on a validation split and the winner is confirmed on an untouched holdout split. The result adds selection_r2 and n_selection. ranking contains validation scores; holdout_r2 determines passed. Code that interpreted ranking values as final confirmation scores must update.

Sequential-design budgets

max_rounds means the maximum adaptive acquisitions after the initial fit, so a completed trace can contain max_rounds + 1 fitted states. Negative, non-integral, and boolean budgets are rejected.

Renamed, retyped, and re-signatured public API

These are source-incompatible: they raise, or silently return something with a different meaning, rather than warning.

  • mixle.epistemic.discrepancy.mmd now returns sqrt(max(mmd_squared, 0)) – a non-negative quantity that behaves like a distance. The unbiased U-statistic it used to return is a signed estimator of squared MMD (it is legitimately negative under the null) and is now mmd_squared, exported from mixle.epistemic alongside mmd. Code performing a two-sample test against a permutation null wants mmd_squared; code reporting a discrepancy magnitude wants mmd. Comparing a stored 0.7.x mmd value against a new one is not meaningful.

  • mixle.evolve.search.search(method="evolutionary") treats n_iter as the TOTAL evaluation budget, matching the bo backend. It previously spent mu + lam * n_iter evaluations, so an unchanged call now does substantially less work. Scale n_iter up if you were relying on the old arithmetic. n_iter <= 0 is now rejected instead of silently spending one evaluation.

  • mixle.fulfillment.route_distribution gained two REQUIRED keyword arguments, demand_nodes and risk. Demand quantity is now chosen by an explicit risk policy and scattered onto named network nodes, instead of both being implicit.

  • PipelineTwin.run() returns lost_sales, lost_sales_cumulative, and demand in place of queue. The old key was misnamed: every period solves against the same nominal demand, so unmet demand is LOST, not backlogged, and reporting it as a growing queue overstated the accumulating shortfall.

  • SystemScorecard no longer has a calibration field and gained question_set_id. The removed field was assigned quality verbatim, so a system emitting no confidence at all scored a perfect calibration. The new field binds a card to the held-out set and judge it was measured with.

  • mixle.system.registry.RegistryEntry is now a frozen dataclass, and registry lookups return defensive copies. Code that mutated a returned entry in place must construct a replacement with dataclasses.replace.

  • Embedder.save() writes a single embedder.mixle envelope rather than a directory of loose files; artifacts written by earlier versions are rejected on load with a message telling you to re-save them.

  • Embedder.transform() raises on a kind="record" list-of-scalars input that was previously accepted and silently produced a wrong result.

Code-execution gates now require an explicit True

Four entry points that decode pickle refused to proceed on a truthy value rather than on True:

load_encoded(path, encoder=enc)                  # now raises: pass trusted=True
load_encoded(path, encoder=enc, trusted=True)    # explicit

Model.load(path, trust_code="false")             # previously OPENED the gate; now raises
Registry.get(name, trust_code=1)                 # now raises; pass True or False
Embedder.load(path, trust_code=True)             # unchanged

trusted=/trust_code= must be the True singleton. A truthy value is deliberately not enough: the string "false" is truthy, and these flags routinely arrive from configuration text.

Boolean flags are exact at public boundaries

bool("false") is True. 101 public flags that previously coerced now require an actual Boolean (bool or numpy.bool_), so passing 0, 1, "true", or "false" raises TypeError. This covers graph directed/self_loops, shuffle, robust, converged, maximize, normalize, low_memory, use_numba, posterior_predictive, truncated, allow_partial, and others. Pass True/False, or convert at your own configuration boundary where you know what the string means.

Durable records are frozen

35 record types are now frozen=True and detach their containers at construction. Rebinding a field raises dataclasses.FrozenInstanceError; mutating a container reached through one no longer changes the record. Use dataclasses.replace(record, field=value) to derive a modified copy.

If you were tampering with a record in place to test integrity checking, reconstruct it with replace instead — that is also the realistic threat, since the integrity check exists for a receipt that was serialized, altered elsewhere, and read back.

These eight remain mutable on purpose, because they are built incrementally rather than constructed complete: GrowthReceipt, ScaleReceipt, ProjectionReceipt, GenerationReport, MetaImprovementReport, SwapRecord, CalibrationReport, MeasurementCatalog.

Verification receipts must name their subject

certify no longer raises a block’s guarantee on a caller-authored receipt that does not identify the model it verified. Bind it:

from mixle.inference import receipt_subject, VerificationReceipt

VerificationReceipt(..., subject_hash=receipt_subject(model))
schedule(model, receipts=[receipt])      # schedule now accepts receipts=

A receipt that names no subject, or a different one, is recorded in BlockPlan.unbound_receipt_ids and does not upgrade anything. BlockPlan.verified_by distinguishes a library-checked guarantee from a caller-asserted one.

Results that will differ

These are corrections, not tuning. Re-baseline anything that recorded the old values.

  • Bayesian-optimization proposals. The local-penalization batch path used an incorrect Expected Improvement whose error was z-dependent, so candidate order could change, not just scores. Max-value entropy search no longer assigns positive information to a deterministic candidate.

  • Exchangeability labels. Multiplicity across fields is corrected, so datasets with several numeric columns that previously reported trend/shift may now report exchangeable. Both raw and adjusted p-values are on each field record, and report.multiplicity states the correction.

  • Expected information gain. The discrepancy-invention loop reports substantially smaller gains and may select a different action; its previous values were not action-conditioned.

  • Multi-fidelity search. budget is now denominated in training tokens rather than rounded to whole epochs, so intermediate budgets execute genuinely less work than before. budget=1.0 is unchanged. Set a seed for comparable recipes.

  • Graph endpoint laws. A declared p=0 or p=1 now yields exactly -inf/0.0 for the corresponding evidence rather than a clipped finite value.

  • Scorecard identities. question_set_id and scorer identities are content-based; identities persisted by an earlier build will not match and should be recomputed.

MILP, flow, and matching inputs are validated

branch_and_bound_milp rejects a non-finite tol (which previously made every integrality test false and could return an objective inconsistent with its own solution), an out-of-range integer index, and a mismatched bounds length. min_cost_flow, max_flow, min_cut, tsp_held_karp, min_arborescence, and the ADMM/subset/colouring routines reject NaN supplies, capacities, and costs, non-square matrices, and asymmetric adjacency. +inf capacities and upper=inf (NNLS) remain supported.

Imports and tests

The root lazy namespace translates only a genuinely absent requested submodule to AttributeError; a missing dependency inside an existing module now propagates with its original cause. Pytest collects both supported filename conventions, which may expose previously skipped tests in custom automation.

Rollback

Pin the previous supported release and restore artifacts produced by its compatible schemas. Do not suppress new validation errors without confirming that the prior accepted behavior was scientifically meaningful.