Skip to content

fix(config): release profile disables JAX, breaking MultiStartAdam validation shard #44

Description

@Jammy2211

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

  • MultiStartAdam.py passes under the release profile and recovers the truth basin
  • Dynesty_jax.py / Nautilus_jax.py verified green with JAX enabled
  • MultiStartAdam.py really executes (not bypassed) under smoke
  • autofit_test/searches shard green; nightly-release reaches Stage 3 clean

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.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions