Summary
nightly-release has failed five nights running (2026-07-11 → 2026-07-15). Stage 3 (release-fidelity integration) dispatches PyAutoHeart workspace-validation.yml at mode=release, and exactly one shard fails — run_scripts (3.12, autofit_test, searches):
scripts/searches/MultiStartAdam.py ... FAIL (1.7s)
ValueError: MultiStartAdam is a JAX-native gradient search and requires a JAX-traceable
Analysis (e.g. `AnalysisImaging(..., use_jax=True)`). The supplied analysis is not running
on the JAX backend.
Latest: nightly 29390030719 → validation 29390440297 (job 87272559383). Every other shard in that run is green.
This is not the shifting perf-flake tail from the release-tail epic — it is a fresh, deterministic regression from the multi-start gradient search promotion (PyAutoFit #1370 + workspace follow-ups, merged 2026-07-14). This repo's HEAD is literally 0a81c45 test(searches): validate MultiStartAdam recovers truth basin under JAX (#43).
Root cause
MultiStartAdam._fit hard-guards on the JAX backend (PyAutoFit .../multi_start_gradient/search.py:120-126):
if not getattr(analysis, "_use_jax", False):
raise ValueError(...)
config/build/env_vars_release.yaml — the mode=release profile, not env_vars.yaml which is the per-PR smoke profile — pins:
PYAUTO_TEST_MODE: "0" → real searches, so AbstractSearch.fit does not take the mode >= 2 bypass (abstract_search.py:683-689) and _fit really runs.
PYAUTO_DISABLE_JAX: "1" → forces use_jax=False, so the script's af.ex.Analysis(..., use_jax=True) is silently flipped and the guard fires.
The profile re-enables JAX for exactly two patterns (jax_assertions/, searches/BlackJAXNUTS). searches/MultiStartAdam was never added.
Why sibling scripts don't fail
autofit_workspace/scripts/searches/mle.py uses af.MultiStartAdam with use_jax=True, but that workspace's env_vars_release.yaml pins PYAUTO_DISABLE_JAX: "0" — JAX on, passes.
autolens_workspace / autogalaxy_workspace guides/modeling/searches.py construct the search but never call .fit() — config-only, guard never reached.
Why the per-PR gate couldn't catch it
Under smoke (env_vars.yaml), PYAUTO_TEST_MODE: "2" bypasses the sampler before _fit, so MultiStartAdam.py passes without ever executing its JAX path. The gate was structurally blind to this.
High-level plan
- Add a release-profile override so
MultiStartAdam runs with JAX enabled — the blocking fix.
- Do the same for
Dynesty_jax / Nautilus_jax, which declare use_jax=True but were also missing from the overrides and so were passing vacuously on the NumPy path.
- Make
MultiStartAdam really execute under the smoke profile, so the per-PR gate can catch this class of regression at PR time instead of on night five.
- Verify every affected script actually passes with JAX on before shipping.
Detailed plan
1. config/build/env_vars_release.yaml — three set: overrides (the profile's header documents that overrides here only ever set:, never unset:, a var defaults pins):
- pattern: "searches/MultiStartAdam"
set: { PYAUTO_DISABLE_JAX: "0" }
- pattern: "searches/Dynesty_jax"
set: { PYAUTO_DISABLE_JAX: "0" }
- pattern: "searches/Nautilus_jax"
set: { PYAUTO_DISABLE_JAX: "0" }
2. config/build/env_vars.yaml (smoke) — mirror the searches/BlackJAXNUTS precedent:
- pattern: "searches/MultiStartAdam"
unset: [PYAUTO_TEST_MODE, PYAUTO_DISABLE_JAX]
Scoped to MultiStartAdam only; the nested samplers stay bypassed under smoke on cost grounds.
Trade-off considered: leaving the smoke profile alone was the leaner option, but it preserves the exact blind spot that let this reach night five. Human's explicit call: it should not stay bypassed if running it is what ensures the bug is caught. Cost is ~6s per PR.
Verification (all run on feature/multistart-adam-release-jax)
Scripts run with the release-profile env (PYAUTO_TEST_MODE=0 PYAUTO_DISABLE_JAX=0 JAX_ENABLE_X64=True):
| Script |
Result |
MultiStartAdam.py |
exit 0 — recovered centre=50.156, normalization=25.196, sigma=9.858 (truth 50/25/10) |
Dynesty_jax.py |
exit 0 (~10s) |
Nautilus_jax.py |
exit 0 (~25s) |
MultiStartAdam.py under the smoke-resolved env (both vars unset): exit 0, same recovery, ~6s.
No bug was hiding behind the two vacuous passes, so all three overrides are safe to ship.
Resolver check — drove autobuild.env_config.build_env_for_script over the real configs for every scripts/searches/*.py, guarding against the substring/.py-anchor over-match trap:
===== env_vars.yaml (smoke) ===== ===== env_vars_release.yaml =====
script TEST_MODE JAX script TEST_MODE JAX
BlackJAXNUTS.py <unset> <unset> BlackJAXNUTS.py 0 0
MultiStartAdam.py <unset> <unset> MultiStartAdam.py 0 0
Dynesty_jax.py 2 1 Dynesty_jax.py 0 0
Nautilus_jax.py 2 1 Nautilus_jax.py 0 0
Nautilus.py 2 1 Nautilus.py 0 1
DynestyStatic.py 2 1 DynestyStatic.py 0 1
DynestyDynamic.py 2 1 DynestyDynamic.py 0 1
Emcee.py / LBFGS.py 2 1 Emcee.py / LBFGS.py 0 1
Zeus.py 2 1 Zeus.py 0 1
Nautilus.py and DynestyStatic/Dynamic.py correctly stay at DISABLE_JAX=1 — the patterns did not over-match.
Acceptance
Prompt: PyAutoMind/active/bug/autofit_workspace_test/multistart_adam_release_profile_disables_jax.md
Branch: feature/multistart-adam-release-jax
Autonomy: supervised (bug cap) — plan written here per the autonomy contract; ship sign-off parks for human review; merge stays human.
Summary
nightly-releasehas failed five nights running (2026-07-11 → 2026-07-15). Stage 3 (release-fidelity integration) dispatches PyAutoHeartworkspace-validation.ymlatmode=release, and exactly one shard fails —run_scripts (3.12, autofit_test, searches):Latest: nightly
29390030719→ validation29390440297(job87272559383). Every other shard in that run is green.This is not the shifting perf-flake tail from the release-tail epic — it is a fresh, deterministic regression from the multi-start gradient search promotion (PyAutoFit #1370 + workspace follow-ups, merged 2026-07-14). This repo's HEAD is literally
0a81c45 test(searches): validate MultiStartAdam recovers truth basin under JAX (#43).Root cause
MultiStartAdam._fithard-guards on the JAX backend (PyAutoFit .../multi_start_gradient/search.py:120-126):config/build/env_vars_release.yaml— themode=releaseprofile, notenv_vars.yamlwhich is the per-PR smoke profile — pins:PYAUTO_TEST_MODE: "0"→ real searches, soAbstractSearch.fitdoes not take themode >= 2bypass (abstract_search.py:683-689) and_fitreally runs.PYAUTO_DISABLE_JAX: "1"→ forcesuse_jax=False, so the script'saf.ex.Analysis(..., use_jax=True)is silently flipped and the guard fires.The profile re-enables JAX for exactly two patterns (
jax_assertions/,searches/BlackJAXNUTS).searches/MultiStartAdamwas never added.Why sibling scripts don't fail
autofit_workspace/scripts/searches/mle.pyusesaf.MultiStartAdamwithuse_jax=True, but that workspace'senv_vars_release.yamlpinsPYAUTO_DISABLE_JAX: "0"— JAX on, passes.autolens_workspace/autogalaxy_workspaceguides/modeling/searches.pyconstruct the search but never call.fit()— config-only, guard never reached.Why the per-PR gate couldn't catch it
Under smoke (
env_vars.yaml),PYAUTO_TEST_MODE: "2"bypasses the sampler before_fit, soMultiStartAdam.pypasses without ever executing its JAX path. The gate was structurally blind to this.High-level plan
MultiStartAdamruns with JAX enabled — the blocking fix.Dynesty_jax/Nautilus_jax, which declareuse_jax=Truebut were also missing from the overrides and so were passing vacuously on the NumPy path.MultiStartAdamreally execute under the smoke profile, so the per-PR gate can catch this class of regression at PR time instead of on night five.Detailed plan
1.
config/build/env_vars_release.yaml— threeset:overrides (the profile's header documents that overrides here only everset:, neverunset:, a vardefaultspins):2.
config/build/env_vars.yaml(smoke) — mirror thesearches/BlackJAXNUTSprecedent:Scoped to
MultiStartAdamonly; the nested samplers stay bypassed under smoke on cost grounds.Trade-off considered: leaving the smoke profile alone was the leaner option, but it preserves the exact blind spot that let this reach night five. Human's explicit call: it should not stay bypassed if running it is what ensures the bug is caught. Cost is ~6s per PR.
Verification (all run on
feature/multistart-adam-release-jax)Scripts run with the release-profile env (
PYAUTO_TEST_MODE=0 PYAUTO_DISABLE_JAX=0 JAX_ENABLE_X64=True):MultiStartAdam.pyDynesty_jax.pyNautilus_jax.pyMultiStartAdam.pyunder the smoke-resolved env (both vars unset): exit 0, same recovery, ~6s.No bug was hiding behind the two vacuous passes, so all three overrides are safe to ship.
Resolver check — drove
autobuild.env_config.build_env_for_scriptover the real configs for everyscripts/searches/*.py, guarding against the substring/.py-anchor over-match trap:Nautilus.pyandDynestyStatic/Dynamic.pycorrectly stay atDISABLE_JAX=1— the patterns did not over-match.Acceptance
MultiStartAdam.pypasses under the release profile and recovers the truth basinDynesty_jax.py/Nautilus_jax.pyverified green with JAX enabledMultiStartAdam.pyreally executes (not bypassed) under smokeautofit_test/searchesshard green;nightly-releasereaches Stage 3 cleanPrompt:
PyAutoMind/active/bug/autofit_workspace_test/multistart_adam_release_profile_disables_jax.mdBranch:
feature/multistart-adam-release-jaxAutonomy: supervised (bug cap) — plan written here per the autonomy contract; ship sign-off parks for human review; merge stays human.