Skip to content

fix: TEST_MODE bypass must evaluate a point the model's assertions accept - #1520

Merged
Jammy2211 merged 1 commit into
mainfrom
feature/test-mode-bypass-assertion-ties
Aug 24, 2026
Merged

fix: TEST_MODE bypass must evaluate a point the model's assertions accept#1520
Jammy2211 merged 1 commit into
mainfrom
feature/test-mode-bypass-assertion-ties

Conversation

@Jammy2211

Copy link
Copy Markdown
Collaborator

Summary

The PYAUTO_TEST_MODE=2/3 bypass has no sampler, so it picks its own evaluation point: the prior medians. A model whose components share priors and carry an ordering assertion — the standard idiom for breaking exchange degeneracy, e.g. PyAutoCTI trap models with trap_0.release_timescale < trap_1.release_timescale — ties exactly there, so check_assertions rejects it with FitException and the run hard-fails on an artifact of the bypass's own choice of point. A real search absorbs this by resampling.

The failure had three sites, not the one reported:

  1. _fit_bypass_test_mode instantiated the model outside the try that catches FitException, so the assertion rejection escaped that guard.
  2. Every fake sample is the median vector scaled uniformly (1.001 / 0.999 / 1.002), and a uniform scale preserves an ordering tie — so no stored sample was reconstructible either.
  3. SamplesSummary.max_log_likelihood is @to_instance() with recover="raise" (it extends SamplesInterface directly, so it does not inherit Samples' next-valid recovery), so result.max_log_likelihood_instance raised SamplesException; Result.instance catches only AttributeError.

Consequence: PYAUTO_TEST_MODE=3 was broken too, even though it never calls the likelihood — it survived the fit and died at the first result.max_log_likelihood_instance.

This fixes all three at the source by making the bypass evaluate and store a vector the model accepts. _test_mode_valid_parameter_vector factors out the deterministic search the TEST_MODE=1 recovery path already used — prior medians first, then np.random.default_rng(seed=0) prior draws, each candidate validated — and both call sites now share it. The fixed seed keeps smoke runs reproducible without touching global random state.

Mode 1's stricter per-sample validation is preserved via the helper's validate hook, so its behaviour and its exact failure message are unchanged. The likelihood-call guard is untouched: a pathological likelihood is a different contract from an invalid instance, and both are now covered.

Unblocks autocti_workspace smoke coverage of modeling/start_here.py-class scripts (CTI resurrection epic, Phase 5), which currently documents this artifact in its AGENTS.md as a workaround.

API Changes

None — internal changes only. No public symbol is added, removed, renamed, or re-signatured; _test_mode_valid_parameter_vector is private.

There is one observable behaviour change worth a reviewer's eye: the bypass now instantiates the model once under PYAUTO_TEST_MODE=3 as well as =2 (previously mode 3 did no instantiation at all). That is what makes the stored vector valid. It means a model whose constructor raises a non-FitException at the medians now fails at fit time rather than at result-access time — the same failure, surfaced earlier. Cost is one instantiation, not one per sample: the 50,000-sample bypass test is unaffected.

See full details below.

Test Plan

  • Reproduced on clean main first — mode 2: FitException: 1 assertions failed! during the fit; mode 3: fit completes, then SamplesException at result.max_log_likelihood_instance.
  • After the fix both modes complete and reconstruct the instance, at the same parameter vector (8.1513753680827, 9.13628021504944) — confirming determinism across modes.
  • pytest test_autofit/non_linear/search/test_abstract_search.py36 passed (31 before, 5 new).
  • All 5 new tests fail against the un-patched source and pass with it, at both modes — verified by reverting only the source file.
  • Full suite pytest test_autofit2016 passed, 34 skipped, 0 failed.

New regression coverage in TestBypassToleratesAssertionTies: a tied ordering assertion completing the fit and reconstructing result.max_log_likelihood_instance at modes 2 and 3; cross-mode determinism of the chosen point; and an unsatisfiable assertion still failing cleanly within the attempt budget, chained to the final rejection.

Full API Changes (for automation & release notes)

Removed

None.

Added

None public. Internal: NonLinearSearch._test_mode_valid_parameter_vector(model, failure_prefix, validate=None) — returns (parameter_vector, validated) for the first deterministically-drawn vector the model accepts; raises FitException chained to the final rejection once TEST_MODE_REPRESENTATIVE_MAX_ATTEMPTS is spent.

Renamed

None. TEST_MODE_REPRESENTATIVE_MAX_ATTEMPTS keeps its name — it is monkeypatched by an existing test.

Changed Signature

None.

Changed Behaviour

  • PYAUTO_TEST_MODE=2 — a model whose assertions reject the prior medians no longer raises during the fit; the bypass evaluates the first accepted deterministic draw instead and logs a WARNING naming the rejection and the draw index.
  • PYAUTO_TEST_MODE=3 — same point selection, so the stored samples are reconstructible and result.max_log_likelihood_instance no longer raises SamplesException. This mode now instantiates the model once (see above).
  • PYAUTO_TEST_MODE=1 — unchanged, including the exact "TEST MODE 1 could not construct a valid representative result after N attempts" message and the per-sample validation.

Migration

None required.

Gate

pyauto-heart is not reachable from this environment (pyauto-brain vitals'pyauto-heart' not found on PATH), so the readiness gate ran in the documented fallback form (PyAutoBrain/skills/WORKFLOW.md): the full library suite as the gate, any failure treated as RED. It came back 2016 passed / 34 skipped / 0 failed. This is not a Heart GREEN verdict — a Heart-reachable environment should confirm before merge.

Closes #1519

Generated by the PyAutoLabs agent workflow.


Generated by Claude Code

…cept

The `PYAUTO_TEST_MODE=2/3` bypass has no sampler, so it picks its own
evaluation point: the prior medians. A model whose components share priors
and carry an ordering assertion — the standard idiom for breaking exchange
degeneracy, e.g. PyAutoCTI trap models with
`trap_0.release_timescale < trap_1.release_timescale` — ties exactly there,
so `check_assertions` rejects it with `FitException` and the run hard-fails
on an artifact of the bypass's own choice of point. A real search absorbs
this by resampling.

The failure had three sites, not one:

- `_fit_bypass_test_mode` instantiated the model outside the `try` that
  catches `FitException`, so the assertion rejection escaped that guard;
- every fake sample is the median vector scaled uniformly, which preserves
  an ordering tie, so no stored sample was reconstructible either;
- `SamplesSummary.max_log_likelihood` is `@to_instance()` with
  `recover="raise"`, so `result.max_log_likelihood_instance` raised
  `SamplesException` — meaning `PYAUTO_TEST_MODE=3` was broken too, even
  though it never calls the likelihood.

Fix all three at the source by making the bypass evaluate *and store* a
vector the model accepts. `_test_mode_valid_parameter_vector` factors out
the deterministic search TEST_MODE=1 recovery already used — prior medians
first, then `default_rng(seed=0)` prior draws, each candidate validated —
and both call sites now share it. The fixed seed keeps smoke runs
reproducible. Mode 1's stronger per-sample validation is preserved via the
`validate` hook.

The likelihood-call guard is untouched: a pathological likelihood is a
different contract from an invalid instance, and both are now covered.

Closes #1519
@Jammy2211 Jammy2211 added the pending-release PR queued for the next release build label Aug 24, 2026 — with Claude
@Jammy2211
Jammy2211 merged commit 438f56f into main Aug 24, 2026
4 checks passed
@Jammy2211
Jammy2211 deleted the feature/test-mode-bypass-assertion-ties branch August 25, 2026 18:14
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

pending-release PR queued for the next release build

Projects

None yet

Development

Successfully merging this pull request may close these issues.

fix: TEST_MODE bypass crashes on ordered-parameter assertion ties

1 participant