Skip to content

refactor: collapse Family-B run_smoke env forks onto shared env_config #185

Description

@Jammy2211

Overview

The env-profile redesign's "one resolver" invariant (docs/env_profile_redesign.md §5) only holds for the four _test repos: six repos — autolens_workspace, autofit_workspace, autogalaxy_workspace, HowToFit, HowToGalaxy, HowToLens — carry hand-forked env resolution inside their vendored .github/scripts/run_smoke.py that omits both the PYAUTO_* scrub and derive_jax_markers support (the five-night-seed failure shape, still live). This task collapses all six forks onto the shared PyAutoHands/autohands/env_config.py, finishes the derivation rollout in autofit_workspace_test, and closes the validator exemption that lets un-migrated repos dodge the strict gate. Phase 1a of the 2026-07-23 test-maintainability plan; unblocks the in-file # ENV: declaration mechanism (Phase 1b, drafted).

Plan

  • Replace the six repos' hand-forked load_env_config/build_env/pattern_matches with imports of the shared resolver (env_config.build_env_for_script), keeping only per-repo wrapper differences (notebook support).
  • Adopt derive_jax_markers: true in autofit_workspace_test/config/build/profile_release.yaml, deleting its four hand-enumerated PYAUTO_DISABLE_JAX overrides (confirm derivation direction — autofit defaults JAX-on).
  • Close the validator exemption: run validate_env_profiles.py (with --strict-derivation --strict-markers) even for workspaces that have no profile_release.yaml.
  • Verify every repo by resolved-env diff (resolve_clean, empty base): must be EMPTY before/after, except deliberately-closed ambient leaks, which are enumerated in each PR body.
  • Archive the superseded aged draft draft/refactor/workspaces/unify_pyauto_env_injection_into_profiles.md after folding in anything still live.
Detailed implementation plan

Affected Repositories

  • PyAutoHands (primary — resolver + validator)
  • autolens_workspace, autofit_workspace, autogalaxy_workspace (Family-B forks, 231/232-line notebook-capable variants)
  • HowToFit, HowToGalaxy, HowToLens (Family-B forks, 112-line script-only variants)
  • autofit_workspace_test (derive_jax_markers adoption)
  • PyAutoHeart (reusable smoke workflow strict gating)

Branch Survey

Repository Current Branch Dirty?
./PyAutoHands main clean
./autolens_workspace main clean
./autofit_workspace main clean
./autogalaxy_workspace main clean
./HowToFit main clean
./HowToGalaxy main clean
./HowToLens main clean
./autofit_workspace_test main clean
./PyAutoHeart main clean

Suggested branch: feature/env-resolver-unification

Worktree root: ~/Code/PyAutoLabs-wt/env-resolver-unification/

Work classification: Workspace (+ infra repos PyAutoHands/PyAutoHeart; no PyAuto* library edits)

Implementation Steps

  1. Baseline capture (before any edit): for each of the 6 Family-B repos, dump the resolved env for every script under the current fork (profile defaults + overrides, base = empty env). Store as the diff baseline. Tooling precedent: PyAutoHands/autohands/validate_env_profiles.py resolve_clean (:52).
  2. Migrate the 3 user workspaces (autolens_workspace, autofit_workspace, autogalaxy_workspace.github/scripts/run_smoke.py, local build_env ~:68): delete the forked load_env_config/build_env/pattern_matches, import env_config (build_env_for_script, load_env_config) instead. PYTHONPATH wiring already exists in the reusable gate (PyAutoHeart/.github/workflows/smoke-tests.yml:108-111). Preserve the notebook-execution wrapper logic untouched.
  3. Migrate the 3 HowTo repos (112-line variants, local build_env ~:52): same collapse; these are byte-identical to each other, so one patch applies three times.
  4. Expected-diff review: post-migration resolved-env diff per repo. The ONLY acceptable rows are those caused by the shared resolver's PYAUTO_* scrub now removing ambient-leak reliance — enumerate them in the PR body; anything else is a migration bug.
  5. autofit_workspace_test derivation adoption: set derive_jax_markers: true in config/build/profile_release.yaml, delete the 4 hand-enumerated PYAUTO_DISABLE_JAX overrides. CAUTION: autofit defaults JAX-on (redesign doc §1) — confirm the profile's PYAUTO_DISABLE_JAX default direction and that is_jax_marked (env_config.py:155; jax_* segment prefix / *_jax / *_jit suffix) matches the intended script set before flipping. Verify with --strict-derivation --strict-markers.
  6. Validator exemption closure (PyAutoHeart/.github/workflows/smoke-tests.yml:85-100): today validation only runs when profile_release.yaml exists, which exempts exactly the Family-B repos. Change to always validate the smoke profile; strict flags on for all migrated repos.
  7. Vendored-copy note: Family A's four wrappers have drifted (autolens_workspace_test has the newer Popen/SIGKILL timeout wrapper from #196, the other three have the older subprocess.run variant). Do NOT unify wrapper behaviour in this task — env resolution only. Record wrapper drift as a hygiene follow-up.
  8. Mind bookkeeping: archive draft/refactor/workspaces/unify_pyauto_env_injection_into_profiles.md (superseded; its SKIP_WORKSPACE_VERSION_CHECK finding shipped with the research: env-profile + validation-gate redesign — make the failure class structurally hard #161 step-3 scrub).

Key Files

  • PyAutoHands/autohands/env_config.py — the shared resolver (build_env_for_script :62, apply_profile :96, is_jax_marked :155, MANAGED_ENV_PREFIXES :28)
  • PyAutoHands/autohands/validate_env_profiles.py — strict guards (resolve_clean :52, DISABLE_JAX override guard :109-116, marker mismatch :118-136)
  • {autolens,autofit,autogalaxy}_workspace/.github/scripts/run_smoke.py — 231/232-line forks to collapse
  • {HowToFit,HowToGalaxy,HowToLens}/.github/scripts/run_smoke.py — 112-line forks to collapse
  • autofit_workspace_test/config/build/profile_release.yaml — derivation adoption
  • PyAutoHeart/.github/workflows/smoke-tests.yml — validator gating (:85-100), PYTHONPATH wiring (:108-111)

Sizing override (recorded)

The Feature Agent scored this too-large (26) off repo count and proposed a generic 4-phase split. Overridden per the recorded repo-count-difficulty-proxy precedent: 9 repos but a uniform mechanical migration verified by empty resolved-env diffs — one task, per-repo PRs, no design phase needed (the design shipped with the env-profile redesign).

Heart status note

Heart is currently RED (release validation failed at integrate, fixes pending merge). This is branch work; the ship gate applies at ship time per the standard flow.

Original Prompt

Click to expand starting prompt

Env resolver unification: collapse the six Family-B run_smoke.py forks onto env_config (Phase 1a)

Type: refactor
Target: workspaces
Repos:

  • autolens_workspace
  • autofit_workspace
  • autogalaxy_workspace
  • HowToFit
  • HowToGalaxy
  • HowToLens
  • autofit_workspace_test
  • PyAutoHands
  • PyAutoHeart
    Difficulty: medium
    Autonomy: supervised
    Priority: high
    Status: formalised

Phase 1a of the maintainability plan from the 2026-07-23 advisory review of the
three "Test Environment Variables / Testing / Test Workspace" issues. Root
cause identified: script env requirements are stored away from scripts, keyed
by path patterns, and the resolution machinery is silently forked.

The finding. The "one resolver" invariant of the env-profile redesign
(PyAutoHands/docs/env_profile_redesign.md §5) only holds for the four _test
repos (Family A), whose vendored .github/scripts/run_smoke.py imports the
shared PyAutoHands/autohands/env_config.py. Six repos (Family B) carry
hand-forked resolution — autolens_workspace, autofit_workspace,
autogalaxy_workspace (231/232-line variants, local build_env ~:68) and
HowToFit/HowToGalaxy/HowToLens (112-line variants, local build_env
~:52). The forks omit BOTH behaviours that only exist in env_config.py:

  1. no PYAUTO_* scrub (MANAGED_ENV_PREFIXES, env_config.py:28, loop :89) —
    ambient vars the profile is silent on leak into script runs (the
    five-night-seed failure shape, still live in these six repos);
  2. no derive_jax_markers support (is_jax_marked, env_config.py:155) — the
    derivation would be silently ignored if enabled in their profiles.

The task.

  1. Migrate all six Family-B run_smoke.py copies to import env_config
    (build_env_for_script, load_env_config), deleting the local forks of
    load_env_config/build_env/pattern_matches. Keep per-repo wrapper
    differences (notebook support in the user workspaces) — only the env
    resolution collapses. PYTHONPATH wiring for the import already exists in
    the reusable gate (PyAutoHeart/.github/workflows/smoke-tests.yml:108-111).
  2. Adopt derive_jax_markers: true in
    autofit_workspace_test/config/build/profile_release.yaml and delete its
    four remaining hand-enumerated PYAUTO_DISABLE_JAX overrides (the last
    Family-A holdout; note autofit defaults JAX-on, so confirm the derivation
    direction is correct there before flipping).
  3. Turn the strict validator on for all migrated repos: --strict-derivation
    and --strict-markers in the reusable smoke workflow. Today
    validate_env_profiles.py only runs when a workspace has a
    profile_release.yaml (smoke-tests.yml:85-100), which exempts exactly the
    Family-B repos — close that exemption (validate the smoke profile even
    where no release profile exists).
  4. Verify by the established gate: resolved-env diff over every script
    (resolve_clean, empty base) must be EMPTY per repo before/after
    migration, except where a leak is being deliberately closed — enumerate
    any non-empty rows in the PR body as the intentional behaviour change.

Supersedes the aged draft
draft/refactor/workspaces/unify_pyauto_env_injection_into_profiles.md
(build-chain campaign, Hands#155, now closed): its smoke-profile
SKIP_WORKSPACE_VERSION_CHECK finding has since shipped (profile-owned as of
the #161 step-3 scrub) and it predates the autoconf/PyAutoBuild renames.
Fold anything still-live from it into this task and archive it.

Why now. This unblocks Phase 1b (in-file # ENV: declarations), which is
a single-point change in apply_profile (env_config.py:96) — but only reaches
repos that actually resolve through it. Evidence trail: July failure census
classified ~7/23 distinct failure signatures as env-pairing drift, and the
whole RED-blocker set was cleared with zero library edits.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions