From e5f2ab600f5ffb9590a3df044dc4e9457936bdad Mon Sep 17 00:00:00 2001 From: Claude Date: Thu, 13 Aug 2026 16:29:15 +0000 Subject: [PATCH] mind: record the autofit all-ones ell_comps bug as shipped Writes complete/2026/08/autofit-mock-all-ones-ell-comps.md via lifecycle.py record, folding the original prompt under 'Original prompt', and removes the draft/ file. The task shipped straight from draft without passing through active/, so nothing had advanced it. The record keeps the three corrected dead ends as traps, the variant table that isolated the library defect from the workspace fixtures, and the cloud-session reproduction recipe. Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_01XqpBSWF2JDx4L9g1YCQk67 --- .../08/autofit-mock-all-ones-ell-comps.md | 305 ++++++++++++++++++ complete/index.md | 3 +- dashboard.md | 7 +- ...l_ones_parameters_break_ell_comps_guard.md | 231 ------------- 4 files changed, 310 insertions(+), 236 deletions(-) create mode 100644 complete/2026/08/autofit-mock-all-ones-ell-comps.md delete mode 100644 draft/bug/autofit/mock_all_ones_parameters_break_ell_comps_guard.md diff --git a/complete/2026/08/autofit-mock-all-ones-ell-comps.md b/complete/2026/08/autofit-mock-all-ones-ell-comps.md new file mode 100644 index 00000000..a9dc6202 --- /dev/null +++ b/complete/2026/08/autofit-mock-all-ones-ell-comps.md @@ -0,0 +1,305 @@ +# autofit-mock-all-ones-ell-comps + +- shipped: 2026-08-13 +- repos: + - PyAutoFit + - PyAutoGalaxy +- PRs: + - PyAutoFit#1471 `aea9a40` — fill mock sample placeholders from prior medians + - PyAutoGalaxy#569 `ee0f471` — let `MockResult` accept and forward `samples_summary` + - PyAutoMind#184 `3cd074e` — correct the prompt's root-cause theory + +## Summary + +PyAutoFit's mock scaffolding filled every parameter with a blanket `1.0`, which +is an invalid `ell_comps` for any elliptical profile (magnitude must be below 1; +`(1.0, 1.0)` is 1.414). Replaced with each prior's median via a shared +`prior_median_kwargs(model)` helper, and widened `ag.m.MockResult` so callers can +actually supply their own summary. + +The reported symptom — 7 aggregator integration scripts failing across +`autogalaxy_workspace_test` and `autolens_workspace_test` — was **already gone** +before this task started. It had been fixed workspace-side on 2026-08-10 18:54 +(`autogalaxy_workspace_test` #104, `autolens_workspace_test` #256), hours after +Heart run 31356506626 sampled it. The ticket was filed against stale evidence. +What shipped here is the library defect those commits worked around. + +## Root cause + +`af.m.MockResult.__init__` falls back to building its own summary when the caller +passes a `model` but no `samples_summary`: + +```python +samples_summary=samples_summary or MockSamplesSummary(model=model or ModelMapper()) +``` + +and `MockSamplesSummary.__init__` filled it with `{path: 1.0 for path in self.model.paths}`. +That dict backs both `max_log_likelihood_sample` and `median_pdf_sample`, so +reading `Result.instance` on such a result raised. + +The crash fires inside `search.fit(...)`, at `mock_search.py:82` +(`if self.result.instance is None`) — **not** in the aggregator, and with no +database round-trip involved. + +## Traps — read these before re-treading + +Three claims in the original prompt were wrong. They are recorded because each +one is a plausible-looking dead end someone will re-derive: + +- **"The path runs through serialization into the database and back out through + the aggregator."** No. The exception precedes `af.Aggregator.from_database`. +- **"`_fit_fast` evaluates at `[prior.mean ...]`, which is 0.0 for `ell_comps` — + valid."** The *vector* is valid. The crash is on the next line, building an + instance from the **summary**. +- **"`MockSamplesSummary.default()` uses an empty `Collection()`, so `_kwargs` is + `{}`."** True, and that is why the search-side summary is a dead end — but the + reaching path is `MockResult`'s fallback `MockSamplesSummary(model=model)`, a + different construction site. + +The original prompt's **"DISPROVEN — do not re-tread"** note about +`parameter_list_with_physical_ell_comps` **stands**: `model.all_paths` and +`model.unique_prior_paths` (used by `Sample.from_lists`) are both sorted by prior +id, so the helper's index alignment is correct. Do not "fix" it. + +Also: the exception type is `ModelParameterException`, not `ValueError`. + +## Second defect found along the way + +`ag.m.MockResult` subclassed `af.m.MockResult` but omitted `samples_summary` from +its signature, so `ag.m.MockResult(..., samples_summary=...)` raised `TypeError`. +Callers could not avoid the bad fallback through it at all — which is why the +workspace fix had to switch to `af.m.MockResult`. `al.m.MockResult` **is** this +same class (re-exported by PyAutoLens, not a second subclass), so one change +covered both. + +## How it was isolated + +Two controlled variants against library `main`, which separate the two +independent causes: + +| Variant | Script parameters | `samples_summary` passed? | Result | +|---|---|---|---| +| Original (pre-fix) | `prior_count * [1.0]`, `* [10.0]` | no | fails `(1.0, 1.0)` — the reported symptom | +| A | `prior_count * [1.0]`, `* [10.0]` | yes | fails `(10.0, 10.0)` — script's own fill | +| B | physical (`ell_comps` → 0.1) | no | fails `(1.0, 1.0)` — **library defect alone** | +| Current `main` | physical | yes | passes | + +Variant B is the decisive one: with physically-valid fixture values the library +fallback still produces the exact reported value, proving the defect is real and +independent of the workspace fixtures. + +## Validation + +| Suite | With fix | Baseline | Verdict | +|---|---|---|---| +| PyAutoFit `test_autofit` | 1698 passed, 1 failed | 1694 passed, 1 failed | +4 new tests, no regression | +| PyAutoGalaxy `test_autogalaxy` | 1081 passed, 0 failed | same | clean | +| PyAutoLens `test_autolens` | 518 passed, 1 failed | same | no regression | +| All 7 reported scripts | 7/7 pass | 7/7 pass | clean | + +Both residual failures are pre-existing and unrelated, and reproduce without the +change: `graphical/functionality/test_messages.py::test_beta` and +`potential_correction/test_iterative_interferometer.py::test__solve_joint_optimization__identity_damping_finite`. + +CI ran the unittest matrix on Python 3.12 and 3.13; both green. + +## Regression cover + +`test_autofit/non_linear/samples/test_mock_placeholders.py` — 4 tests built on a +guard class mirroring the `ell_comps` constraint, so the regression is covered +inside PyAutoFit with no `autogalaxy` dependency. Verified to fail 3/4 against +unpatched code. + +## Reproducing the full stack from a cloud session + +The original prompt claimed the autogalaxy stack "could not be installed" in a +cloud session and that reproduction needed a local environment. **It can be**, and +this whole task was done that way: + +```bash +python3.12 -m venv venv && ./venv/bin/pip install autolens # pulls the dependency closure +./venv/bin/pip uninstall -y autofit autogalaxy autoarray autonerves autolens +export PYTHONPATH=:::: +export PYAUTO_SKIP_WORKSPACE_VERSION_CHECK=1 +cd autogalaxy_workspace_test/scripts/misc/aggregator && python ellipse.py +``` + +Install the released stack first for its dependency closure, then shadow the +libraries with source checkouts via `PYTHONPATH`. The `PYTHONPATH` route (rather +than `pip install -e`) matters: an editable install is refused on Python 3.11 +because `autonerves` now requires `>=3.12`. To reproduce the original failure, +check out `autogalaxy_workspace_test` at `40beb30^`. + +## Sizing note + +Filed as `medium`; the Bug Agent scored it **too-large (17)** on a 3-repo +library+workspace coordination. Both were wrong once the workspace half turned +out to be already shipped — the actual change was **small**: one helper, three +call sites, one signature. + +## Not done + +Optional tidiness: having `MockSearch` inherit `samples_summary` from a passed-in +`result` rather than silently defaulting to `MockSamplesSummary.default()`. No +longer a correctness issue after this fix, and it touches ~55 `MockSearch` call +sites, so it belongs in its own behaviour-preserving change. + +## Original prompt + +# PyAutoFit mock scaffolding fills every parameter with 1.0, which is now an invalid `ell_comps` + +Type: bug +Target: PyAutoFit +Repos: +- PyAutoFit +- autogalaxy_workspace_test +- autolens_workspace_test +Difficulty: medium +Autonomy: supervised +Priority: high +Status: formalised — NOT started. Root cause is narrowed to PyAutoFit's mock + helpers but the exact call site is **not** pinned; see "What is not yet + known". Requires an environment that can run the full autogalaxy stack. + +## Symptom + +Seven aggregator integration scripts across the two `*_workspace_test` repos fail +with the same exception: + + ValueError: ell_comps must satisfy ell_comps[0]**2 + ell_comps[1]**2 < 1; + got (1.0, 1.0), whose magnitude is np.float64(1.4142135623730951) + +Observed in PyAutoHeart Workspace Smoke run 31356506626 (2026-08-10), legs +`autogalaxy_test / misc` and `autolens_test / misc`. + +Failing scripts (all `scripts/misc/aggregator/`): + +- `autogalaxy_workspace_test`: `ellipse.py`, `fit_imaging.py`, + `fit_interferometer.py`, `galaxies.py` +- `autolens_workspace_test`: `tracer.py`, `fit_imaging.py`, + `fit_interferometer.py` + +## Why this is a real bug and not a bad shipped value + +The guard is correct and correctly placed. `validate_ell_comps` sits on +`EllProfile`, the single base every elliptical profile inherits (`ag.Ellipse` +included), and enforces `f = sqrt(e_y**2 + e_x**2) < 1` because the axis ratio is +`q = (1 - f) / (1 + f)` — at `f >= 1` the ellipse degenerates to `q <= 0` and has +no geometric meaning. `(1.0, 1.0)` gives `f = 1.414`, `q = -0.17`. Nothing should +ever construct a profile with it. + +This is **not** the same failure as the sampler-draw legs (`guides`, etc.), which +were fixed by PyAutoGalaxy#568 making `ModelParameterException` a +`FitException` so searches resample. That fix does not help here: the aggregator +rebuilds instances from stored samples outside any likelihood call, so there is no +resample path to take. + +## Evidence + +1. **The value is a hardcoded fill, not a sampled value.** It prints as plain + `(1.0, 1.0)` — Python floats. The genuine sampler-draw failures in the same run + print as `np.float64(-0.7446446619131553)`. Different provenance. + +2. **Two places in PyAutoFit hardcode exactly this shape:** + - `autofit/non_linear/mock/mock_samples.py` — `MockSamples.default_sample_list` + builds `kwargs={path: 1.0 for path in self.model.paths}`. + - `autofit/non_linear/mock/mock_samples_summary.py` — `MockSamplesSummary.__init__` + sets `self._kwargs = {path: 1.0 for path in self.model.paths}`, which backs + both `max_log_likelihood_sample` and `median_pdf_sample`. + + A blanket `1.0` is a safe placeholder for most parameters and an invalid value + for any `ell_comps`. `_make_samples` in `mock_search.py` already does the right + thing (`prior.value_for(0.5)`), so the fix idiom exists in the same package. + +3. **All 7 scripts construct `MockSearch` identically and never pass + `samples_summary`:** + + ```python + search = ag.m.MockSearch( + samples=samples, + result=af.m.MockResult(model=model, samples=samples, + samples_summary=samples.summary()), + ) + ``` + + `MockSearch.__init__` therefore falls back to `MockSamplesSummary.default()`. + Whether that asymmetry (a real model in `MockResult`, a default summary on the + search) is the trigger or a red herring is the open question. + +## Hypothesis already tested and DISPROVEN — do not re-tread + +The obvious suspect is the helper copy-pasted into all 7 scripts: + +```python +def parameter_list_with_physical_ell_comps(value): + parameter_list = model.prior_count * [value] + for index, path_tuple in enumerate(model.all_paths): + if "ell_comps" in path_tuple[0]: + parameter_list[index] = 0.1 + return parameter_list +``` + +It looks broken — `all_paths` returns a tuple of `Path`s per prior and +`Path = Tuple[str, ...]`, so `path_tuple[0]` is a path tuple and `in` is +exact-element membership, which would not match a leaf named `ell_comps_0`. + +**It is not broken.** `ell_comps` has a tuple default, so PyAutoFit builds a +`TuplePrior` attribute named `ell_comps`, and the path is +`('ellipses', '0', 'ell_comps', 'ell_comps_0')` — it contains a bare `'ell_comps'` +element, so the check matches. Reproduced by rebuilding `ellipse.py`'s exact model +(two `Ellipse` models with fixed `major_axis`, plus the nested multipole +collection) against installed autofit and running the real helper: + +``` +[2] ('ellipses', '0', 'ell_comps', 'ell_comps_0') -> 0.1 +[3] ('ellipses', '0', 'ell_comps', 'ell_comps_1') -> 0.1 +ellipses[0].ell_comps = (0.1, 0.1) +``` + +Index alignment is also fine: `all_paths` and `instance_from_vector` +(`prior_tuples_ordered_by_id`) both order by prior id. **Changing this helper is a +no-op — do not "fix" it.** + +## What is not yet known + +Which call site actually feeds the all-ones instance to the aggregator. Two +candidates were checked and neither fits cleanly: + +- `MockSamplesSummary.default()` uses an empty `Collection()`, so its `_kwargs` + is `{}`, not a dict of 1.0s. +- `MockSearch._fit_fast` evaluates at `[prior.mean for prior in + model.priors_ordered_by_id]`, which is `0.0` for `ell_comps` — valid. + +So the path runs through serialization into the database and back out through the +aggregator, which is where it needs to be traced. + +## Suggested approach + +1. Run one failing script (`autogalaxy_workspace_test/scripts/misc/aggregator/ellipse.py`) + against the full stack with a breakpoint or traceback on the guard, and record + the actual construction stack. **This needs a real autogalaxy environment** — + it could not be done from a cloud session (autoarray/jax/numba would not + install there). +2. Fix at the PyAutoFit mock layer: replace the blanket `{path: 1.0 ...}` with + prior-median values (`{path: prior.value_for(0.5) for path, prior in + model.path_priors_tuples}`), matching `_make_samples`. +3. Mind the blast radius: `MockSamples`, `MockSamplesSummary` and `MockSearch` + have roughly 55 call sites inside PyAutoFit alone, plus the PyAutoGalaxy and + PyAutoLens suites. Run all three suites, not just PyAutoFit's. +4. Consider whether `MockSearch` should inherit the `samples_summary` from a + passed-in `result` rather than silently defaulting. + +## Notes + +- Do not relax or move the `ell_comps` guard. It is correct. +- Do not chase the `workspace-validation-report` artifact from a cloud session + (blocked at the egress proxy). Per-job logs via the Actions API carry the same + failures. +- Sibling work already shipped: the one genuinely unphysical shipped literal, + `ell_comps=(0.5, 0.9)` in HowToGalaxy `tutorial_3_fitting`, was corrected + separately. An AST scan of 454 `ell_comps` literals across + autogalaxy_workspace, autolens_workspace, HowToGalaxy, HowToLens and both + `*_workspace_test` repos found no other violating literal, so this ticket is + the whole remaining `ell_comps` surface. +- PyAutoHeart#27 is a different family (release-profile timeouts and a JAX + exception, 2026-07-06); it is not related. diff --git a/complete/index.md b/complete/index.md index 15a69070..7f9abbee 100644 --- a/complete/index.md +++ b/complete/index.md @@ -6,7 +6,7 @@ Token-light navigation over the finished-work records (schema: only then grep a dated bucket. Curators: edit the band between the CURATED markers; everything below GENERATED is rebuilt. -973 records across 7 buckets. +974 records across 7 buckets. ## Highlights @@ -23,6 +23,7 @@ _(curate hard-won records here — survives regeneration.)_ - [autoarray-adapt-images-precondition](2026/08/autoarray-adapt-images-precondition.md) - [autoarray-input-validation-guards](2026/08/autoarray-input-validation-guards.md) - [autocti-util-dataset-export](2026/08/autocti-util-dataset-export.md) — no GitHub issue — worked directly from the draft prompt in a cloud session on explicit human instruction; the … +- [autofit-mock-all-ones-ell-comps](2026/08/autofit-mock-all-ones-ell-comps.md) - [autogalaxy-assistant](2026/08/autogalaxy-assistant.md) — epic; human to close - [autogalaxy-profile-validation-guards](2026/08/autogalaxy-profile-validation-guards.md) - [autohands-firewall-allowlist](2026/08/autohands-firewall-allowlist.md) — auto-closed by the merge diff --git a/dashboard.md b/dashboard.md index 9592a05e..459c9139 100644 --- a/dashboard.md +++ b/dashboard.md @@ -11,7 +11,7 @@ Tasks only — the organism's health lives with the Heart (`/health`), not here. | [In flight](#in-flight) (`active/`) | 8 | | [Parked](#parked) (`parked.md`) | 6 | | [Planned](#planned) (`planned.md`) | 7 | -| [Backlog](#backlog) (`draft/`) | 136 | +| [Backlog](#backlog) (`draft/`) | 135 | Live on GitHub: [open issues](https://github.com/search?q=org%3APyAutoLabs+is%3Aissue+is%3Aopen&type=issues) · [open pull requests](https://github.com/search?q=org%3APyAutoLabs+is%3Apr+is%3Aopen&type=prs) @@ -95,10 +95,10 @@ Scoped but not started; some are not yet prompt files. Full detail in [`planned. ## Backlog -**136** filed prompts, not started. Each section is sorted most-pickable first (priority, then size). +**135** filed prompts, not started. Each section is sorted most-pickable first (priority, then size).
-bug — 40 +bug — 39 - [pre_build stages untracked files, publishing uncommitted human work](draft/bug/pyautohands/pre_build_stages_untracked_wip.md) — pyautohands · small · supervised · high - [Release does not sync __version__ stamps and workspace pins back](draft/bug/pyautobuild/release_version_sync_back_to_main.md) — pyautobuild · medium · supervised · high @@ -111,7 +111,6 @@ Scoped but not started; some are not yet prompt files. Full detail in [`planned. - [Fix release-profile numerical inversion failures](draft/bug/health_fixes/numerical_inversion_failures.md) — health_fixes · too-large · supervised · high - [Fix release result/sample parameter-path regressions](draft/bug/health_fixes/samples_parameter_paths.md) — health_fixes · too-large · supervised · high - [`pixel_scales` given as an `int` (or `np.float64`) is never widened](draft/bug/autoarray/pixel_scales_int_not_widened_to_tuple.md) — autoarray · small · supervised · medium -- [PyAutoFit `MockResult` fills every parameter with 1.0, which is an](draft/bug/autofit/mock_all_ones_parameters_break_ell_comps_guard.md) — autofit · small · supervised · medium - [Heart script_timing baselines are orphaned by path moves and filled](draft/bug/pyautoheart/script_timing_baselines_orphaned_and_window_filled.md) — pyautoheart · small · supervised · medium - [jax_grad scripts fail assertions locally that PASS in CI](draft/bug/autolens_workspace_test/jax_grad_local_assertions_fail_but_pass_in_ci.md) — autolens_workspace_test · medium · supervised · medium - [ConstantZeroth regularization is broken twice over — dead code presenting](draft/bug/autoarray/constant_zeroth_broken_dead_code.md) — autoarray · small · supervised · normal diff --git a/draft/bug/autofit/mock_all_ones_parameters_break_ell_comps_guard.md b/draft/bug/autofit/mock_all_ones_parameters_break_ell_comps_guard.md deleted file mode 100644 index 04473cf9..00000000 --- a/draft/bug/autofit/mock_all_ones_parameters_break_ell_comps_guard.md +++ /dev/null @@ -1,231 +0,0 @@ -# PyAutoFit `MockResult` fills every parameter with 1.0, which is an invalid `ell_comps` - -Type: bug -Target: PyAutoFit -Repos: -- PyAutoFit -Difficulty: small -Autonomy: supervised -Priority: medium -Status: **shipped** (2026-08-13). PyAutoFit#1471 (fix + 4 regression tests) and - PyAutoGalaxy#569 (`MockResult` signature) both merged to `main`. - The originally-reported symptom was already gone before this work — it was - fixed in the workspace repos on 2026-08-10, hours after the Heart run that - reported it. This ticket fixed the underlying library defect. - -## TL;DR — what changed since this prompt was first filed - -1. **The 7 failing scripts all pass now.** They were fixed in the workspace repos - (`autogalaxy_workspace_test` #104 and `autolens_workspace_test` #256, both - committed 2026-08-10 18:54 -0400) — after Heart run 31356506626 sampled them. - This ticket was filed against stale evidence. -2. **The library defect those commits worked around is still on `main`**, and is - the real content of this ticket. -3. **Reproduction is no longer blocked.** The prompt previously said the full - autogalaxy stack "could not be installed from a cloud session". It can — see - "How to reproduce" below. Everything in this document was produced that way. -4. Three claims in the original prompt were wrong; they are corrected below so - nobody re-treads them. - -## Root cause (reproduced, not inferred) - -`af.m.MockResult.__init__` falls back to building its own summary when the caller -does not pass one: - -```python -# autofit/non_linear/mock/mock_result.py -super().__init__( - samples_summary=samples_summary or MockSamplesSummary(model=model or ModelMapper()), - ... -) -``` - -and `MockSamplesSummary.__init__` fills every parameter with a blanket `1.0`: - -```python -# autofit/non_linear/mock/mock_samples_summary.py:26 -self._kwargs = {path: 1.0 for path in self.model.paths} if self.model else {} -``` - -That dict backs both `max_log_likelihood_sample` and `median_pdf_sample`. So any -`MockResult` constructed with a real `model` but no `samples_summary` carries an -all-ones instance — and `1.0` is an invalid `ell_comps` for every elliptical -profile. - -**The consumer is `Result.instance`, and the crash happens inside `search.fit()` — -not in the aggregator.** Verified traceback (pre-fix `ellipse.py`, libraries at -`main`): - -``` -ellipse.py:53 search.fit(model=model, analysis=analysis) -abstract_search.py:739 search_internal, fitness = self._fit(...) -mock_search.py:101 return self._fit_fast(model=model, analysis=analysis) -mock_search.py:95 fitness([prior.mean for prior in model.priors_ordered_by_id]) -mock_search.py:82 if self.result.instance is None: <-- here -result.py:117 return self.samples_summary.instance -... -geometry_profiles.py:237 validate.validate_ell_comps(ell_comps=ell_comps) -autogalaxy.exc.ModelParameterException: ell_comps must satisfy - ell_comps[0]**2 + ell_comps[1]**2 < 1; got (1.0, 1.0), magnitude 1.4142135623730951 -``` - -## Corrections to the original prompt — do not re-tread these - -- **"The path runs through serialization into the database and back out through - the aggregator."** Wrong. The exception is raised during `search.fit(...)`, - before `af.Aggregator.from_database` is ever called. No database round-trip is - involved. -- **"`MockSearch._fit_fast` evaluates at `[prior.mean ...]`, which is 0.0 for - `ell_comps` — valid."** The *vector* is indeed valid. The crash is on the next - line (`mock_search.py:82`, `self.result.instance`), which builds an instance - from the **summary**, not from the vector. -- **"`MockSamplesSummary.default()` uses an empty `Collection()`, so its `_kwargs` - is `{}`."** Correct, and that is exactly why the search-side summary was a dead - end. The reaching path is `MockResult`'s fallback `MockSamplesSummary(model=model)` - — a *different* construction site that was never checked. -- The exception type is `ModelParameterException`, not `ValueError`. -- The original "DISPROVEN — do not re-tread" note about - `parameter_list_with_physical_ell_comps` **stands**: `model.all_paths` and - `model.unique_prior_paths` (used by `Sample.from_lists`) are both sorted by - prior id, so the helper's index alignment is correct. - -## Second, independent defect: `ag.m.MockResult` narrows its parent's API - -```python -# autogalaxy/analysis/mock/mock_result.py -class MockResult(af.m.MockResult): - def __init__(self, samples=None, instance=None, model=None, - analysis=None, search=None, - max_log_likelihood_galaxies=None, max_log_likelihood_tracer=None): -``` - -`samples_summary` is absent from the signature and is not forwarded, so -`ag.m.MockResult(..., samples_summary=...)` raises -`TypeError: MockResult.__init__() got an unexpected keyword argument 'samples_summary'`. -Callers therefore *cannot* avoid the all-ones fallback through `ag.m.MockResult` -at all — which is why the workspace fix had to switch to `af.m.MockResult`. -Confirmed by direct experiment. - -## Evidence: two controlled variants isolate the two causes - -Both run against library `main` with the pre-fix or post-fix `ellipse.py`: - -| Variant | Script parameters | `samples_summary` passed? | Result | -|---|---|---|---| -| Original (pre-fix) | `prior_count * [1.0]`, `* [10.0]` | no | fails at `(1.0, 1.0)` — **the reported symptom** | -| A | `prior_count * [1.0]`, `* [10.0]` | yes | fails at `(10.0, 10.0)` — script's own fill | -| B | physical (`ell_comps` → 0.1) | no | fails at `(1.0, 1.0)` — **library defect alone** | -| Current `main` | physical | yes | passes | - -Variant B is the decisive one: with physically-valid fixture values, the library -fallback still produces the exact reported `(1.0, 1.0)`. The library defect is -real and independent of the workspace fixture values. - -## The fix (written and validated) - -```diff ---- a/autofit/non_linear/mock/mock_samples_summary.py -+++ b/autofit/non_linear/mock/mock_samples_summary.py -@@ -23,7 +23,11 @@ class MockSamplesSummary(SamplesSummary): - self._max_log_likelihood_instance = max_log_likelihood_instance - self._prior_means = prior_means -- self._kwargs = {path: 1.0 for path in self.model.paths} if self.model else {} -+ self._kwargs = ( -+ {path: prior.value_for(0.5) for path, prior in self.model.path_priors_tuples} -+ if self.model -+ else {} -+ ) -``` - -This matches the idiom already used by `_make_samples` in `mock_search.py` -(`prior.value_for(0.5)`), so the fix is consistent with the package's own -convention rather than a new one. - -### Validation actually run (libraries at `main`, Python 3.11 venv) - -| Suite | With fix | Baseline (no fix) | Verdict | -|---|---|---|---| -| PyAutoFit `test_autofit` | 1694 passed, 1 failed | 1694 passed, 1 failed | identical — no regression | -| PyAutoGalaxy `test_autogalaxy` | 1081 passed, 0 failed | — | clean | -| PyAutoLens `test_autolens` | 518 passed, 1 failed | 1 failed | identical — no regression | -| All 7 reported scripts | 7/7 pass | 7/7 pass | clean | -| Variant B (fixture fix reverted) | passes | fails `(1.0, 1.0)` | fix is load-bearing | - -The two pre-existing failures are unrelated and reproduce without the patch: -`test_autofit/graphical/functionality/test_messages.py::test_beta` and -`test_autolens/potential_correction/test_iterative_interferometer.py::test__solve_joint_optimization__identity_damping_finite`. - -## What was implemented - -Branch `claude/autofit-mock-ones-parameters-bug-sv303m` in both repos. - -**PyAutoFit** (`2581ecf`): -1. New shared helper `prior_median_kwargs(model)` in `mock_samples.py`. -2. `MockSamplesSummary.__init__` and `MockSamples.default_sample_list` both use - it instead of `{path: 1.0 ...}`. `_make_samples` in `mock_search.py` now - delegates to it too — the idiom it already used, in one place rather than three. -3. New `test_autofit/non_linear/samples/test_mock_placeholders.py` — 4 tests using - a guard class that mirrors the `ell_comps` constraint, so the regression is - covered inside PyAutoFit with no autogalaxy dependency. Verified to fail 3/4 - without the fix. - -**PyAutoGalaxy** (`96baf25`): `MockResult.__init__` accepts `samples_summary` and -forwards it to `super()`. `al.m.MockResult` *is* `ag.m.MockResult` (re-exported, -not a second subclass), so PyAutoLens is covered by the same change. - -### Post-implementation validation - -| Suite | Result | Baseline | Verdict | -|---|---|---|---| -| PyAutoFit | 1698 passed, 1 failed | 1694 passed, 1 failed | +4 new tests, no regression | -| PyAutoGalaxy | 1081 passed, 0 failed | 1081 passed, 0 failed | clean | -| PyAutoLens | 518 passed, 1 failed | 518 passed, 1 failed | no regression | -| All 7 reported scripts | 7/7 pass | 7/7 pass | clean | -| Variant B | passes | fails `(1.0, 1.0)` | fix is load-bearing | - -The latent call site `test_autogalaxy/analysis/analysis/test_analysis.py:40` is -green under the full PyAutoGalaxy suite above. - -## Remaining scope - -Optional tidiness only (original suggestion 4): have `MockSearch` inherit -`samples_summary` from a passed-in `result` instead of silently defaulting to -`MockSamplesSummary.default()`. With the fix above this is no longer a -correctness issue. Not done — it touches ~55 `MockSearch` call sites and belongs -in its own behaviour-preserving change. - -**Difficulty is `small`, not `too-large`.** The original sizing assumed a 3-repo -library+workspace coordination. The workspace half is already shipped; what is -left is a self-contained PyAutoFit change (plus an optional small PyAutoGalaxy -one), with all three library suites already shown green against it. - -## How to reproduce (this works from a cloud session) - -```bash -python3.12 -m venv venv && ./venv/bin/pip install autolens # pulls the full stack -./venv/bin/pip uninstall -y autofit autogalaxy autoarray autonerves autolens -# then put the source checkouts on PYTHONPATH: -export PYTHONPATH=:::: -export PYAUTO_SKIP_WORKSPACE_VERSION_CHECK=1 -cd autogalaxy_workspace_test/scripts/misc/aggregator && python ellipse.py -``` - -Install the released stack first to get the dependency closure, then shadow the -four libraries with source checkouts via `PYTHONPATH` — an editable install of the -checkouts is refused on Python 3.11 because `autonerves` now requires `>=3.12`, -and the `PYTHONPATH` route sidesteps that gate. To reproduce the original failure, -check out `autogalaxy_workspace_test` at `40beb30^`. - -## Notes - -- Do not relax or move the `ell_comps` guard. It is correct. -- The workspace-side fixes (#104, #256) are legitimate and should stay: these are - integration fixtures, and physically-valid fixture values are the right thing - regardless of the library defect. They are not masking — after the library fix, - variant B shows the scripts pass on their own merit either way. -- PyAutoHeart#27 is a different family (release-profile timeouts and a JAX - exception, 2026-07-06); it is not related. -- Sibling work already shipped: the one genuinely unphysical shipped literal, - `ell_comps=(0.5, 0.9)` in HowToGalaxy `tutorial_3_fitting`, was corrected - separately. An AST scan of 454 `ell_comps` literals across the workspace repos - found no other violating literal.