# 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](0.8.1.md). ## 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`: ```python 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: ```python 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.