From 046acb2b55c26b1560510bdb16d85b0ea66cdb91 Mon Sep 17 00:00:00 2001 From: Claude Date: Thu, 27 Aug 2026 16:31:52 +0000 Subject: [PATCH 1/4] prompt: retire loggaussian-prior-declares-own-support (PyAutoFit#1526) The fix shipped 2026-08-25 as PyAutoFit#1527 (merged, `34d6dff`, `pending-release`), closing PyAutoFit#1526. The Mind-side close-out never landed: that session's PyAutoMind branch `claude/loggaussian-prior-support-buv5xe` was condemned the same day, so the prompt stayed in `draft/bug/autofit/` and kept rendering on the dashboard as pickable backlog. Writes the completion record from PyAutoFit#1526/#1527 and from `main` itself, retires the prompt, refreshes `complete/index.md` and regenerates the dashboard. No PyAutoFit changes. Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_0173PAAaCQ7myTu9SUboYA4Y --- .../loggaussian-prior-declares-own-support.md | 214 ++++++++++++++++++ complete/index.md | 3 +- dashboard.html | 19 +- dashboard.md | 18 +- .../loggaussian_prior_declares_own_support.md | 75 ------ 5 files changed, 230 insertions(+), 99 deletions(-) create mode 100644 complete/2026/08/loggaussian-prior-declares-own-support.md delete mode 100644 draft/bug/autofit/loggaussian_prior_declares_own_support.md diff --git a/complete/2026/08/loggaussian-prior-declares-own-support.md b/complete/2026/08/loggaussian-prior-declares-own-support.md new file mode 100644 index 00000000..ede4d23e --- /dev/null +++ b/complete/2026/08/loggaussian-prior-declares-own-support.md @@ -0,0 +1,214 @@ +- issue: https://github.com/PyAutoLabs/PyAutoFit/issues/1526 (closed by the PR's `Closes` line) +- completed: 2026-08-25 +- pr: https://github.com/PyAutoLabs/PyAutoFit/pull/1527 (MERGED, merge `34d6dff`, head `c3505d1`, + +276/-34 over 6 files, label `pending-release`) +- summary: `LogGaussianPrior` now declares its own `(0, inf)` support instead of delegating a wrong + `(-inf, inf)` to a `TransformedMessage` that was never given limits. `Prior` gains a general + strictness contract (`lower_limit_strict` / `upper_limit_strict`) so a consumer can tell an + *exclusive* bound from an inclusive one without a type switch, and `Prior.limits` is now derived + from `lower_limit`/`upper_limit` rather than hardcoded — the actual root cause of the bug class. + `ClipperPriorBox`'s `isinstance(prior, LogGaussianPrior)` workaround, its import and its + workaround docstrings are retired. This is follow-up 3 owed by + `complete/2026/08/prior-support-clipper.md` (PyAutoFit#1477). +- validation: full suite 2178 passed / 36 skipped (baseline on `main`: 2124 / 36). +- release: not performed; the merged PR sits in the pending-release queue. + +> **RECORD WRITTEN LATE, 2026-08-27.** The code shipped 2026-08-25 but the Mind-side close-out +> never landed: that session's PyAutoMind branch (`claude/loggaussian-prior-support-buv5xe` @ +> `5dfffb4c3`) was condemned the same day and archived to +> `archive/condemned/pyautomind-loggaussian-prior-support-buv5xe` (see `condemned.md`). The prompt +> was therefore still sitting in `draft/bug/autofit/`, rendering on the dashboard as pickable +> backlog, and a `/start_dev` run on 2026-08-27 rediscovered the work already merged. This record +> and the `draft/ → complete/` move are that close-out, reconstructed from PyAutoFit#1526/#1527 and +> from `main` itself. Nothing in PyAutoFit was changed to write it. + +## What shipped + +| File | Change | +|---|---| +| `autofit/mapper/prior/abstract.py` | `Prior.lower_limit_strict` / `upper_limit_strict` class attributes (both `False`); `Prior.limits` derived from `lower_limit`/`upper_limit` | +| `autofit/mapper/prior/log_gaussian.py` | `lower_limit_strict = True`; `__init__` sets `self.lower_limit = 0.0`, `self.upper_limit = inf` | +| `autofit/non_linear/clipper.py` | `_limits_from_model` reads the strictness flags; `isinstance` block, `LogGaussianPrior` import and workaround docstrings gone | +| `test_autofit/mapper/prior/test_log_gaussian.py` | +128: reported support, strictness, regression pins | +| `test_autofit/mapper/prior/test_prior_properties.py` | +80: property P6 over every prior family | +| `test_autofit/non_linear/test_clipper.py` | the test that **asserted the bug** rewritten | + +## The design decision worth remembering: on the prior, never on the message + +The prompt offered two routes — "pass the limits into the `TransformedMessage`, or override +`lower_limit`". Only the second is viable, and the reason is not stylistic: + +- Limits set on the message are **dropped** by `with_base`, `copy`, `project` and `__call__`. +- They would change what `MeanField.lower_limit` and `LaplaceOptimiser(check_limits=True)` feed to + `OptimisationState.valid` — a live EP/Laplace behaviour change, well outside a bug fix. + +So `message.lower_limit` is deliberately left at `±inf` and the declaration is shadowed on the +prior instance. That is why the EP machinery sees exactly what it saw before. + +The second half of the fix is the one that stops this recurring: **`Prior.limits` used to be a +hardcoded `(-inf, inf)`** for every prior that did not override it. Two notions of "support" that +can disagree by construction is the bug class; deriving one from the other closes it. + +## The general test is the deliverable, not the fix + +`test_prior_properties.py` P6 asserts, parametrised over **every** prior family, that + +- `log_prior_from_value` is finite strictly inside the *reported* `limits`, +- `-inf` outside them, and +- `-inf` *at* a bound flagged strict, finite at one that is not. + +It **fails on the parent commit for `LogGaussianPrior` alone** and passes for the other five +families — i.e. it is the general form of the bug, not a restatement of the fix. That check is what +would have caught this in #1477, and it is what will catch the next prior that misreports. + +## Measured: 34 of 37 probes identical + +Before/after over 37 behavioural probes against a running 3.12 install. The three that moved are +`lower_limit` under `copy`, `pickle` and `project` — the fix itself. + +| Probe | Result | +|---|---| +| `Identifier(prior)` + full `description` | byte-identical — **no output directories re-key** | +| `Identifier(LBFGS(clipper=ClipperPriorBox()))` | byte-identical | +| `log_prior_from_value` at 11 points either side of `0` | pointwise identical | +| `value_for`, `unit_value_for` round-trip, `Model.vector_from_unit_vector` | identical over a 10-point unit grid | +| `ClipperPriorBox` bounds / projections / clipped-masks | identical, 3 clipper configs × 3 input vectors | +| `message.lower_limit` (what EP/Laplace read) | unchanged at `±inf` | +| `Gaussian` / `Uniform` / `LogUniform` / `TruncatedGaussian` limits | unchanged | + +The identifier result is the one to note. The prompt flagged re-keying as the main hazard, citing +the clipper-identifier decision of 2026-08-18 (`complete/2026/08/clipper-in-search-identifier.md`), +which chose to re-key and orphan stored results. **This change did not have to make that trade**: +`__identifier_fields__ = ("mean", "sigma")` gates it, and the strictness flags are *class* +attributes, so they stay out of `__dict__` and out of the hash. Declaring a derived constant on a +prior is identifier-safe; adding an instance attribute inside `__identifier_fields__` would not be. + +## The one thing that DID change downstream — prior passing + +Where a model's config supplies no `Limits` entry for a LogGaussian parameter, +`AbstractPriorModel` falls back to `prior.limits`: + +| | before | after | +|---|---|---| +| passed prior | `TruncatedGaussian(1.0, 0.5, -inf, inf)` | `TruncatedGaussian(1.0, 0.5, 0.0, inf)` | +| `value_for(0.001)` | **`-0.545`** | `0.0089` | +| `log_prior_from_value(-1.0)` | **`-8.0`** (finite) | `-inf` | + +A strictly positive parameter was being passed a prior that samples negative values. The fix is a +correctness fix, but it **changes the unit-cube mapping of the passed prior** — inter-phase prior +passing in PyAutoGalaxy / PyAutoLens is worth a spot-check, and that spot-check was not done as +part of #1527. + +## Follow-ups still open + +1. **`OptimisationState.valid` is falsy at `0.0`.** `line_search.py` uses + `if self.lower_limit and …`, which now silently skips the check for exactly the prior this task + gave a `0.0` lower limit. Pre-existing, untouched, and the most likely of the three to bite. +2. **Three redundant `limits` overrides.** `UniformPrior`, `LogUniformPrior` and + `TruncatedGaussianPrior` now duplicate the base implementation exactly. Trivial tidy-up. +3. **Declaring limits on `TransformedMessage`.** Rejected here for the EP reasons above; if the + message stack ever needs to carry its own support, it is its own task with its own EP + regression work. +4. **Downstream prior-passing spot-check** in PyAutoGalaxy / PyAutoLens, per the section above. + +Nothing above was filed as a prompt at the time of writing this record. + +## Repos / worktree + +- PyAutoFit: `feature/loggaussian-prior-support` (merged, deletable). +- No worktree — the implementing session ran `web-github` against a direct clone. +- PyAutoMind: the implementing session's branch was condemned before its Mind state merged; this + record was written on `claude/loggaussian-prior-support-ngh59x` two days later. + +## The process lesson: a condemned Mind branch orphans the close-out silently + +The code half of this task shipped cleanly and the Mind half vanished with the branch. Nothing +detected it: `dashboard.md` self-heals its *render*, and a prompt that shipped but was never +retired renders faithfully as pickable backlog — no workflow can tell the difference. The drift +surfaced only because `/start_dev` was pointed at the prompt again and the session checked the +tracker before filing a duplicate issue. + +Two cheap habits fall out of that. **Check the issue tracker and the target repo's `main` before +filing** — start_dev's resume check reads `active.md`, which by construction cannot know about a +close-out that never merged. And when condemning a branch, **check whether it carried Mind state +that has no other home**; `condemned.md` recorded the branch faithfully, but "what was lost with +it" is not a field it has. + +## Original prompt + +# `LogGaussianPrior` misreports its own support as `(-inf, inf)` + +Type: bug +Target: autofit +Repos: +- PyAutoFit +Difficulty: small +Autonomy: supervised +Priority: normal +Status: formalised +Filed: 2026-08-16 (backfilled from git) +Issued: 2026-08-25 + +Filed 2026-08-16. Follow-up 3 owed by the prior-support `Clipper` +(`complete/2026/08/prior-support-clipper.md`, PyAutoFit#1477), which worked +around it rather than fixing it. + +## The defect + +`LogGaussianPrior`'s support is `(0, inf)` — `log_prior_from_value` returns +`-inf` for `value <= 0`. But its `TransformedMessage` defaults its limits to +`±inf` and is never passed any, so the prior **reports** `(-inf, inf)`. + +Every other prior answers `lower_limit` / `upper_limit` truthfully via +`Prior.__getattr__` delegating to the message, which is why the `Clipper` needs +no type switch anywhere else. This one prior is the exception, and it is the +kind of exception that is invisible until something trusts the answer. + +## Why it matters now + +`ClipperPriorBox` **declares the real support in the clipper** rather than on +the prior — deliberately, to avoid touching a shared class late in that task, +and recorded as a follow-up rather than left silent. That special case is +correct but misplaced: any future consumer of `lower_limit` gets the wrong +answer unless it also knows to special-case this prior. + +The general hazard: a bound of `-inf` on a strictly positive parameter means a +consumer will not guard `0`, and `log(0)` / a division by it is the failure that +follows. + +## The fix + +Declare the support on `LogGaussianPrior` itself — pass the limits into the +`TransformedMessage`, or override `lower_limit` — then retire the clipper's +special case and its accompanying comment. + +## The care needed — why this is `supervised` and not `safe` + +Changing what a prior reports as its support is not local: + +- **The nested samplers work in unit-cube coordinates** and map through the + prior. Confirm a limits change does not alter that mapping, or every stored + nested-sampling result shifts. +- **`log_prior_from_value` must not change behaviour.** It is already correct; + only the *reported* limits are wrong. If the fix changes the density anywhere, + it has gone too far. +- **Check the identifier.** If `lower_limit` feeds the search identifier, a + change re-keys existing output directories and orphans stored results — the + same class of concern as the clipper identifier decision, which chose to + re-key and orphan rather than special-case (2026-08-18; record + `complete/2026/08/clipper-in-search-identifier.md`). + +## Verify + +- `LogGaussianPrior(...).lower_limit == 0.0` (or whatever exclusive convention + is chosen — state it). +- `log_prior_from_value` is unchanged across a range of values either side of + zero, asserted against the pre-change values. +- `ClipperPriorBox.bounds_from_model` returns the same bounds for a model + containing a `LogGaussianPrior` **after** the clipper's special case is + removed as it did before — that equivalence is the whole point of the change. +- A nested-sampler unit-cube round-trip through the prior is unchanged. + + diff --git a/complete/index.md b/complete/index.md index 9b79da84..3d921b17 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. -1153 records across 7 buckets. +1154 records across 7 buckets. ## Highlights @@ -133,6 +133,7 @@ _(curate hard-won records here — survives regeneration.)_ - [llms-txt-census-fixes](2026/08/llms-txt-census-fixes.md) - [local-pixel-scale-vs-dataset-pixel-scales](2026/08/local-pixel-scale-vs-dataset-pixel-scales.md) - [log-det-multistart-tag](2026/08/log-det-multistart-tag.md) +- [loggaussian-prior-declares-own-support](2026/08/loggaussian-prior-declares-own-support.md) — closed by the PR's `Closes` line - [markdown-renderings-2a-leftovers](2026/08/markdown-renderings-2a-leftovers.md) - [mask1d-shape-native-scalar-widening](2026/08/mask1d-shape-native-scalar-widening.md) - [memory-surfaces-stale-names](2026/08/memory-surfaces-stale-names.md) — auto-closed on merge diff --git a/dashboard.html b/dashboard.html index 85f95f1a..aa8c00d2 100644 --- a/dashboard.html +++ b/dashboard.html @@ -204,7 +204,7 @@

PyAutoMindDashboard

Intent. Priority. Flow.

Every task the Mind is holding. Tap a task's 📋 and its /start_dev command is on your clipboard — paste it into a Claude Code chat to route Claude straight to that task. Recent is the same work by date — what has been happening rather than what to do next.

- +

Last updated 2026-08-27. This page is generated from active/, draft/ and the registry files, so it is only as current as they are. dashboard_refresh.yml re-renders it on every push to main — that heals a stale page, but not a stale prompt: a task that shipped without its prompt advancing to complete/ keeps rendering here as pickable backlog. Reconciling those is the refresh below.

latent-nan-guard-honest-run — planned 2026-07-22

Backlog markdown version

-

136 filed prompts, not started — sorted most-pickable first (priority, then size). 25 of them belong to an epic and are listed only under Epics below.

+

135 filed prompts, not started — sorted most-pickable first (priority, then size). 25 of them belong to an epic and are listed only under Epics below.

feature — 28 @@ -344,12 +344,11 @@

Backlog

Regenerate setup_notebook-drifted notebooks in autogalaxy/autofit/HowToFit workspaces🧹 maintenanceworkspacessmallsupervisedlow

-bug — 17 +bug — 16

Fix release JAX runtime compatibility and likelihood parity🐛 bughealth_fixestoo-largesupervisedhigh

Fix JIT quick-update visualization output regressions🐛 bughealth_fixestoo-largesupervisedhigh

Fix release result/sample parameter-path regressions🐛 bughealth_fixestoo-largesupervisedhigh

-

LogGaussianPrior misreports its own support as (-inf, inf)🐛 bugautofitsmallsupervisednormal

@@ -616,12 +615,6 @@

Backlog -2026-08-16 -filed -LogGaussianPrior misreports its own support as (-inf, inf) - - - 2026-08-14 filed Three jax_likelihood pins are stale by ~1.24e-4 and fail the smoke… @@ -711,6 +704,12 @@

Backlog Un-park imaging/features/scaling_relation/slam — the PyAutoArray#431… + +2026-07-31 +filed +interferometer/start_here.py OOM in nightly release-validation… + +

Epics markdown version

diff --git a/dashboard.md b/dashboard.md index cc89c9a0..fc98d195 100644 --- a/dashboard.md +++ b/dashboard.md @@ -45,7 +45,7 @@ anything you could not verify. | [In flight](#in-flight) (`active/`) | 1 | | [Parked](#parked) (`parked.md`) | 3 | | [Planned](#planned) (`planned.md`) | 5 | -| [Backlog](#backlog) (`draft/`) | 136 | +| [Backlog](#backlog) (`draft/`) | 135 | ## Start here @@ -247,7 +247,7 @@ 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). **25** of them belong to an epic and are listed only under [Epics](#epics) below. +**135** filed prompts, not started. Each section is sorted most-pickable first (priority, then size). **25** of them belong to an epic and are listed only under [Epics](#epics) below.
feature — 28 @@ -777,7 +777,7 @@ Scoped but not started; some are not yet prompt files. Full detail in [`planned.
-bug — 17 +bug — 16
📋 LensCalc NumPy Hessian step is too coarse for multi-plane tracers — autogalaxy · large · supervised · high @@ -811,14 +811,6 @@ Scoped but not started; some are not yet prompt files. Full detail in [`planned.
-
📋 LogGaussianPrior misreports its own support as (-inf, inf) — autofit · small · supervised · normal - -``` -/start_dev draft/bug/autofit/loggaussian_prior_declares_own_support.md -``` - -
-
📋 Three jax_likelihood pins are stale by ~1.24e-4 and fail the smoke gate… — workspaces · small · supervised · normal ``` @@ -1237,18 +1229,17 @@ The 50 newest things to happen to the work in hand, newest first — issued, par | 2026-08-18 | filed | @PyAutoFit TransformedMessage.logpdf/pdf omit the transform… | | 2026-08-17 | filed | Which other searches need prior-support handling — coverage audit… | | 2026-08-17 | filed | Teach repos_sync --write to stamp organ config surfaces | -| 2026-08-16 | filed | LogGaussianPrior misreports its own support as (-inf, inf) | | 2026-08-14 | filed | Three jax_likelihood pins are stale by ~1.24e-4 and fail the smoke… | | 2026-08-09 | found | isothermal-ell-sph-oversampling-at-the-cusp | | 2026-08-08 | parked | pyautoreduce-slacs1430-acs-comparison | | 2026-08-08 | filed | Regenerate autolens_workspace markdown/ so the MGE pages show… | | 2026-08-07 | filed | Regenerate setup_notebook-drifted notebooks in… | +| 2026-08-06 | filed | Triage: Convolver "No blurring_image provided" warning in canonical… |
… 10 more (10 left) | Date | Event | Task | |------|-------|------| -| 2026-08-06 | filed | Triage: Convolver "No blurring_image provided" warning in canonical… | | 2026-08-06 | filed | Rewrite PyAutoCTI docs/api — 55 of 89 autosummary entries are dead | | 2026-08-06 | filed | Dependency-cap refresh 2026-08: safe bumps, astropy 8 decision, two… | | 2026-08-05 | filed | Give PyAutoFit searches a seed — today no search can be made… | @@ -1258,6 +1249,7 @@ The 50 newest things to happen to the work in hand, newest first — issued, par | 2026-08-04 | filed | Nightly release has been blocked 8 nights running — triage the streak | | 2026-08-04 | filed | HowToLens ch4 tutorial 3: mask overlay is never actually drawn | | 2026-08-03 | filed | Un-park imaging/features/scaling_relation/slam — the PyAutoArray#431… | +| 2026-07-31 | filed | interferometer/start_here.py OOM in nightly release-validation… |
diff --git a/draft/bug/autofit/loggaussian_prior_declares_own_support.md b/draft/bug/autofit/loggaussian_prior_declares_own_support.md deleted file mode 100644 index 3f7b0050..00000000 --- a/draft/bug/autofit/loggaussian_prior_declares_own_support.md +++ /dev/null @@ -1,75 +0,0 @@ -# `LogGaussianPrior` misreports its own support as `(-inf, inf)` - -Type: bug -Target: autofit -Repos: -- PyAutoFit -Difficulty: small -Autonomy: supervised -Priority: normal -Status: formalised -Filed: 2026-08-16 (backfilled from git) - -Filed 2026-08-16. Follow-up 3 owed by the prior-support `Clipper` -(`complete/2026/08/prior-support-clipper.md`, PyAutoFit#1477), which worked -around it rather than fixing it. - -## The defect - -`LogGaussianPrior`'s support is `(0, inf)` — `log_prior_from_value` returns -`-inf` for `value <= 0`. But its `TransformedMessage` defaults its limits to -`±inf` and is never passed any, so the prior **reports** `(-inf, inf)`. - -Every other prior answers `lower_limit` / `upper_limit` truthfully via -`Prior.__getattr__` delegating to the message, which is why the `Clipper` needs -no type switch anywhere else. This one prior is the exception, and it is the -kind of exception that is invisible until something trusts the answer. - -## Why it matters now - -`ClipperPriorBox` **declares the real support in the clipper** rather than on -the prior — deliberately, to avoid touching a shared class late in that task, -and recorded as a follow-up rather than left silent. That special case is -correct but misplaced: any future consumer of `lower_limit` gets the wrong -answer unless it also knows to special-case this prior. - -The general hazard: a bound of `-inf` on a strictly positive parameter means a -consumer will not guard `0`, and `log(0)` / a division by it is the failure that -follows. - -## The fix - -Declare the support on `LogGaussianPrior` itself — pass the limits into the -`TransformedMessage`, or override `lower_limit` — then retire the clipper's -special case and its accompanying comment. - -## The care needed — why this is `supervised` and not `safe` - -Changing what a prior reports as its support is not local: - -- **The nested samplers work in unit-cube coordinates** and map through the - prior. Confirm a limits change does not alter that mapping, or every stored - nested-sampling result shifts. -- **`log_prior_from_value` must not change behaviour.** It is already correct; - only the *reported* limits are wrong. If the fix changes the density anywhere, - it has gone too far. -- **Check the identifier.** If `lower_limit` feeds the search identifier, a - change re-keys existing output directories and orphans stored results — the - same class of concern as the clipper identifier decision, which chose to - re-key and orphan rather than special-case (2026-08-18; record - `complete/2026/08/clipper-in-search-identifier.md`). - -## Verify - -- `LogGaussianPrior(...).lower_limit == 0.0` (or whatever exclusive convention - is chosen — state it). -- `log_prior_from_value` is unchanged across a range of values either side of - zero, asserted against the pre-change values. -- `ClipperPriorBox.bounds_from_model` returns the same bounds for a model - containing a `LogGaussianPrior` **after** the clipper's special case is - removed as it did before — that equivalence is the whole point of the change. -- A nested-sampler unit-cube round-trip through the prior is unchanged. - - From ba71d5c2de3c3704474c01a7a81145260d3d6f3c Mon Sep 17 00:00:00 2001 From: Claude Date: Thu, 27 Aug 2026 16:50:03 +0000 Subject: [PATCH 2/4] intake: file the four PyAutoFit#1527 follow-ups MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Filed from the follow-up list in complete/2026/08/loggaussian-prior-declares-own-support.md: - refactor/autofit/optimisation_state_limit_guard_truthiness.md - refactor/autofit/redundant_prior_limits_overrides.md - research/graphical_ep/transformed_message_declares_support.md - test/autogalaxy/prior_passing_loggaussian_lower_bound.md Also corrects that record's follow-up 1. PyAutoFit#1527 describes `OptimisationState.valid`'s guard as "falsy at 0.0", implying the fix disabled a limit check for the prior it gave a 0.0 lower limit. It did not: `lower_limit` there is a `VariableData` dict, so the guard tests non-emptiness, and the EP path reads limits off the message, which #1527 left at +/-inf. What remains is a readability defect, not a live bug — the prompt is filed as refactor on that basis. Follow-up 2 likewise gains the `float()` coercion caveat that makes it a type change rather than a pure deletion. Dashboard regenerated in the same commit; `lifecycle.py check` OK. Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_0173PAAaCQ7myTu9SUboYA4Y --- .../loggaussian-prior-declares-own-support.md | 31 ++++-- dashboard.html | 73 +++++++------- dashboard.md | 98 ++++++++++++++----- ...timisation_state_limit_guard_truthiness.md | 86 ++++++++++++++++ .../redundant_prior_limits_overrides.md | 68 +++++++++++++ .../transformed_message_declares_support.md | 65 ++++++++++++ .../prior_passing_loggaussian_lower_bound.md | 70 +++++++++++++ 7 files changed, 423 insertions(+), 68 deletions(-) create mode 100644 draft/refactor/autofit/optimisation_state_limit_guard_truthiness.md create mode 100644 draft/refactor/autofit/redundant_prior_limits_overrides.md create mode 100644 draft/research/graphical_ep/transformed_message_declares_support.md create mode 100644 draft/test/autogalaxy/prior_passing_loggaussian_lower_bound.md diff --git a/complete/2026/08/loggaussian-prior-declares-own-support.md b/complete/2026/08/loggaussian-prior-declares-own-support.md index ede4d23e..cd0318a5 100644 --- a/complete/2026/08/loggaussian-prior-declares-own-support.md +++ b/complete/2026/08/loggaussian-prior-declares-own-support.md @@ -101,17 +101,32 @@ part of #1527. ## Follow-ups still open -1. **`OptimisationState.valid` is falsy at `0.0`.** `line_search.py` uses - `if self.lower_limit and …`, which now silently skips the check for exactly the prior this task - gave a `0.0` lower limit. Pre-existing, untouched, and the most likely of the three to bite. +1. **`OptimisationState.valid`'s truthiness guard** — `line_search.py:107-114` uses + `if self.lower_limit and …`. Filed as + `draft/refactor/autofit/optimisation_state_limit_guard_truthiness.md`. + + **#1527's own description of this one is wrong, and the correction is the useful part.** Its + follow-up list calls the guard "falsy at `0.0`", implying the fix above silently disabled a + limit check for exactly the prior it gave a `0.0` lower limit. It did not. `self.lower_limit` + is a `VariableData` (`autofit/mapper/variable.py:309`, a `Dict` subclass) built by + `MeanField.lower_limit` and passed only when `check_limits=True`; dict truthiness is + non-emptiness, so the guard is `False` only for a model with no free variables, where the check + is vacuous. There is no `0.0` for it to be falsy at. And the EP path reads `m.lower_limit` off + the **message**, which this task deliberately left at `±inf` — so that code sees exactly what + it saw before. What remains is a readability/robustness defect: the guard reads as a scalar + test (which is how it entered the follow-up list as a bug) and is one type change away from + being one. 2. **Three redundant `limits` overrides.** `UniformPrior`, `LogUniformPrior` and - `TruncatedGaussianPrior` now duplicate the base implementation exactly. Trivial tidy-up. -3. **Declaring limits on `TransformedMessage`.** Rejected here for the EP reasons above; if the - message stack ever needs to carry its own support, it is its own task with its own EP - regression work. + `TruncatedGaussianPrior` now duplicate the base implementation exactly — except that the base + coerces with `float()` and they do not, so deletion is a type change, not a no-op. Filed as + `draft/refactor/autofit/redundant_prior_limits_overrides.md`. +3. **Declaring limits on `TransformedMessage`.** Rejected here for the EP reasons above. The + residue is that the prior and its message now deliberately disagree about the support. Filed as + `draft/research/graphical_ep/transformed_message_declares_support.md`. 4. **Downstream prior-passing spot-check** in PyAutoGalaxy / PyAutoLens, per the section above. + Filed as `draft/test/autogalaxy/prior_passing_loggaussian_lower_bound.md`. -Nothing above was filed as a prompt at the time of writing this record. +All four were filed as prompts on 2026-08-27, alongside this record. ## Repos / worktree diff --git a/dashboard.html b/dashboard.html index aa8c00d2..99c2128f 100644 --- a/dashboard.html +++ b/dashboard.html @@ -204,7 +204,7 @@

PyAutoMindDashboard

Intent. Priority. Flow.

Every task the Mind is holding. Tap a task's 📋 and its /start_dev command is on your clipboard — paste it into a Claude Code chat to route Claude straight to that task. Recent is the same work by date — what has been happening rather than what to do next.

-
  • 1In flight
  • 3Parked
  • 5Planned
  • 135Backlog
+
  • 1In flight
  • 3Parked
  • 5Planned
  • 139Backlog

Last updated 2026-08-27. This page is generated from active/, draft/ and the registry files, so it is only as current as they are. dashboard_refresh.yml re-renders it on every push to main — that heals a stale page, but not a stale prompt: a task that shipped without its prompt advancing to complete/ keeps rendering here as pickable backlog. Reconciling those is the refresh below.

Split lensing regimes: multi_galaxy / group / cluster (epic plan)📖 docsautolenstoo-largesupervisedhigh

Re-baseline the MGE imaging JIT profiling regression value🧪 testautolens_workspace_developertoo-largesupervisedhigh

Quick wins (small enough, and safe enough to run unattended)

-

(none right now)

+ +

In flight markdown version

Issued — each has an open GitHub issue and usually a branch.

@PyAutoFit TransformedMessage.factor_gradient crashes on first callissue #1501 — issued 2026-08-19HOLD — do not start dev. Fix-or-delete hangs off the PyAutoFit#1498 logpdf-contract

@@ -269,7 +270,7 @@

Planned

latent-nan-guard-honest-run — planned 2026-07-22

Backlog markdown version

-

135 filed prompts, not started — sorted most-pickable first (priority, then size). 25 of them belong to an epic and are listed only under Epics below.

+

139 filed prompts, not started — sorted most-pickable first (priority, then size). 25 of them belong to an epic and are listed only under Epics below.

feature — 28 @@ -302,7 +303,7 @@

Backlog

Teach repos_sync --write to stamp organ config surfaces✨ featurepyautomindhardsupervisedlow

-research — 14 +research — 15

Deep research: Can we speed up Delaunay in PyAutoArray?🔬 researchautoarraytoo-largesupervisedhigh

Delaunay-family JAX modules never hit the persistent compilation cache🔬 researchautoarraymediumsupervisedmedium

@@ -314,6 +315,7 @@

Backlog

Checkerboard PSF-mismatch residual diagnostic — research + document + ingest papers🔬 researchpyautomemorymediumsupervisednormal

Multi-band compile census completion — A100/multi-core + hetero GPU rows🔬 researchautolens_profilingsmallsupervisedlow

+

Should TransformedMessage carry its own support, rather than the prior?🔬 researchgraphical_epmediumhuman-requiredlow

@@ -378,19 +380,22 @@

Backlog

plot coverage — follow-ups deferred from plot-coverage-gaps📖 docsworkspaces

-refactor — 6 +refactor — 8

Split Fitness.batch_size into lh_batch_size and latent_batch_size♻️ refactorautofitsmallsupervisednormal

One construction path for plane-bound lensing quantities♻️ refactorautolensmediumsupervisednormal

Remove the dead EDEN packaging tooling from PyAutoFit♻️ refactorpyautofitmediumsupervisednormal

+ +

Deduplicate repos_sync.py's check/write pairs♻️ refactorpyautomindmediumsafelow

-test — 6 +test — 7

Cross-validate multi-plane ray tracing🧪 testautolenslargesupervisedhigh

Re-baseline the MGE imaging JIT profiling regression value🧪 testautolens_workspace_developertoo-largesupervisedhigh

+

Relevance-gate the reusable smoke workflow so a PR only runs…🧪 testpyautoheartmediumsupervisednormal

@@ -413,6 +418,30 @@

Backlog 2026-08-27 filed +OptimisationState.valid guards on dict truthiness, not on "is a… + + + +2026-08-27 +filed +Three limits overrides are now exact duplicates of Prior.limits + + + +2026-08-27 +filed +Spot-check downstream prior passing after LogGaussianPrior gained a… + + + +2026-08-27 +filed +Should TransformedMessage carry its own support, rather than the… + + + +2026-08-27 +filed One construction path for plane-bound lensing quantities @@ -446,25 +475,25 @@

Backlog The Brain board should work in a session that has no gh - + 2026-08-24 filed wiki-currency's --check-version gate rots on every library main merge - + 2026-08-24 filed interferometer/jax_grad/gradient.py: eager and jitted likelihoods… - + 2026-08-24 filed autocti_workspace has no Navigator Check, so its CI can never roll… - + 2026-08-24 filed Un-park multi_galaxy/features/scaling_relation/slam once a capped run… @@ -686,30 +715,6 @@

Backlog autolens_workspace_developer: broad stale-API rot (56 symbols, no CI) - -2026-08-04 -filed -Nightly release has been blocked 8 nights running — triage the streak - - - -2026-08-04 -filed -HowToLens ch4 tutorial 3: mask overlay is never actually drawn - - - -2026-08-03 -filed -Un-park imaging/features/scaling_relation/slam — the PyAutoArray#431… - - - -2026-07-31 -filed -interferometer/start_here.py OOM in nightly release-validation… - -

Epics markdown version

diff --git a/dashboard.md b/dashboard.md index fc98d195..b37f04f5 100644 --- a/dashboard.md +++ b/dashboard.md @@ -45,7 +45,7 @@ anything you could not verify. | [In flight](#in-flight) (`active/`) | 1 | | [Parked](#parked) (`parked.md`) | 3 | | [Planned](#planned) (`planned.md`) | 5 | -| [Backlog](#backlog) (`draft/`) | 135 | +| [Backlog](#backlog) (`draft/`) | 139 | ## Start here @@ -149,7 +149,21 @@ anything you could not verify. **Quick wins** (small enough, and safe enough to run unattended) -- _(none right now)_ +
📋 OptimisationState.valid guards on dict truthiness, not on "is a limit set" — autofit · small · safe · low + +``` +/start_dev draft/refactor/autofit/optimisation_state_limit_guard_truthiness.md +``` + +
+ +
📋 Three limits overrides are now exact duplicates of Prior.limits — autofit · small · safe · low + +``` +/start_dev draft/refactor/autofit/redundant_prior_limits_overrides.md +``` + +
## In flight @@ -247,7 +261,7 @@ Scoped but not started; some are not yet prompt files. Full detail in [`planned. ## Backlog -**135** filed prompts, not started. Each section is sorted most-pickable first (priority, then size). **25** of them belong to an epic and are listed only under [Epics](#epics) below. +**139** filed prompts, not started. Each section is sorted most-pickable first (priority, then size). **25** of them belong to an epic and are listed only under [Epics](#epics) below.
feature — 28 @@ -479,7 +493,7 @@ Scoped but not started; some are not yet prompt files. Full detail in [`planned.
-research — 14 +research — 15
📋 Deep research: Can we speed up Delaunay in PyAutoArray? — autoarray · too-large · supervised · high @@ -569,6 +583,14 @@ Scoped but not started; some are not yet prompt files. Full detail in [`planned.
+
📋 Should TransformedMessage carry its own support, rather than the prior? — graphical_ep · medium · human-required · low + +``` +/start_dev draft/research/graphical_ep/transformed_message_declares_support.md +``` + +
+
📋 Chase the ~6% flux scale between PyAutoReduce and legacy SLACS reductions — pyautoreduce · medium · supervised · low ``` @@ -1011,7 +1033,7 @@ Scoped but not started; some are not yet prompt files. Full detail in [`planned.
-refactor — 6 +refactor — 8
📋 einstein_radius_jit_from: replace static init_guess with a JAX-native seed finder — autogalaxy · too-large · supervised · high @@ -1053,6 +1075,22 @@ Scoped but not started; some are not yet prompt files. Full detail in [`planned.
+
📋 OptimisationState.valid guards on dict truthiness, not on "is a limit set" — autofit · small · safe · low + +``` +/start_dev draft/refactor/autofit/optimisation_state_limit_guard_truthiness.md +``` + +
+ +
📋 Three limits overrides are now exact duplicates of Prior.limits — autofit · small · safe · low + +``` +/start_dev draft/refactor/autofit/redundant_prior_limits_overrides.md +``` + +
+
📋 Deduplicate repos_sync.py's check/write pairs — pyautomind · medium · safe · low ``` @@ -1064,7 +1102,7 @@ Scoped but not started; some are not yet prompt files. Full detail in [`planned.
-test — 6 +test — 7
📋 Cross-validate multi-plane ray tracing — autolens · large · supervised · high @@ -1090,6 +1128,14 @@ Scoped but not started; some are not yet prompt files. Full detail in [`planned.
+
📋 Spot-check downstream prior passing after LogGaussianPrior gained a 0.0 lower limit — autogalaxy · small · supervised · normal + +``` +/start_dev draft/test/autogalaxy/prior_passing_loggaussian_lower_bound.md +``` + +
+
📋 Relevance-gate the reusable smoke workflow so a PR only runs… — pyautoheart · medium · supervised · normal ``` @@ -1180,76 +1226,76 @@ The 50 newest things to happen to the work in hand, newest first — issued, par | Date | Event | Task | |------|-------|------| +| 2026-08-27 | filed | OptimisationState.valid guards on dict truthiness, not on "is a… | +| 2026-08-27 | filed | Three limits overrides are now exact duplicates of Prior.limits | +| 2026-08-27 | filed | Spot-check downstream prior passing after LogGaussianPrior gained a… | +| 2026-08-27 | filed | Should TransformedMessage carry its own support, rather than the… | | 2026-08-27 | filed | One construction path for plane-bound lensing quantities | | 2026-08-27 | filed | Multi-plane time delays | | 2026-08-27 | filed | LensCalc NumPy Hessian step is too coarse for multi-plane tracers | | 2026-08-27 | filed | Cross-validate multi-plane ray tracing | | 2026-08-26 | filed | jax_profiling/gradient/imaging/pixelization.py: 3.2% of its pin move… | | 2026-08-26 | filed | The Brain board should work in a session that has no gh | -| 2026-08-24 | filed | wiki-currency's --check-version gate rots on every library main merge | -| 2026-08-24 | filed | interferometer/jax_grad/gradient.py: eager and jitted likelihoods… | -| 2026-08-24 | filed | autocti_workspace has no Navigator Check, so its CI can never roll… | -| 2026-08-24 | filed | Un-park multi_galaxy/features/scaling_relation/slam once a capped run… |
… 10 more (40 left) | Date | Event | Task | |------|-------|------| +| 2026-08-24 | filed | wiki-currency's --check-version gate rots on every library main merge | +| 2026-08-24 | filed | interferometer/jax_grad/gradient.py: eager and jitted likelihoods… | +| 2026-08-24 | filed | autocti_workspace has no Navigator Check, so its CI can never roll… | +| 2026-08-24 | filed | Un-park multi_galaxy/features/scaling_relation/slam once a capped run… | | 2026-08-24 | filed | Induct PyAutoReduce into the PyAutoHands release machinery (date… | | 2026-08-24 | filed | Heart's local smoke runner cannot run any CTI workspace — no autocti… | | 2026-08-23 | filed | pynufft removal: unswept downstream residue (1 hard break + stale… | | 2026-08-23 | filed | Phase 3: stop installing pynufft in Hands/Heart CI and PyAutoCTI… | | 2026-08-23 | filed | Brain board follow-ups: what real mornings surface | | 2026-08-22 | filed | smoke_install.sh's stale jax<0.7 pin — CI is on the right jax… | -| 2026-08-22 | filed | Untrack the generated FITS test artifacts in autoarray | -| 2026-08-22 | filed | The reconstruction noise map describes a different estimator than the… | -| 2026-08-22 | filed | Point-source JSON datasets record no resolution regime | -| 2026-08-22 | filed | Is Intel macOS a supported platform, and what is the numpy-only… |
… 10 more (30 left) | Date | Event | Task | |------|-------|------| +| 2026-08-22 | filed | Untrack the generated FITS test artifacts in autoarray | +| 2026-08-22 | filed | The reconstruction noise map describes a different estimator than the… | +| 2026-08-22 | filed | Point-source JSON datasets record no resolution regime | +| 2026-08-22 | filed | Is Intel macOS a supported platform, and what is the numpy-only… | | 2026-08-21 | filed | Rectangular mesh split: Bilinear (fast CPU default) vs RTU… | | 2026-08-20 | filed | Numba CPU likelihood phase 2: kernel-CDF numba fast path (the 49-88%… | | 2026-08-20 | filed | Numba CPU likelihood phase 1: batched MGE convolution +… | | 2026-08-19 | filed | autofit_profiling: bootstrap the repo + general PyAutoFit profiling… | | 2026-08-19 | issued | @PyAutoFit TransformedMessage.factor_gradient crashes on first… | | 2026-08-19 | filed | Release board: local run_logs enrichment | -| 2026-08-19 | filed | RTD organism docs currency: Nerves page, organ-count drift, hands.md… | -| 2026-08-19 | filed | Deduplicate repos_sync.py's check/write pairs | -| 2026-08-19 | filed | Bug in autocti_workspace: the dataset_1d results/database example… | -| 2026-08-18 | parked | single-source-density-design |
… 10 more (20 left) | Date | Event | Task | |------|-------|------| +| 2026-08-19 | filed | RTD organism docs currency: Nerves page, organ-count drift, hands.md… | +| 2026-08-19 | filed | Deduplicate repos_sync.py's check/write pairs | +| 2026-08-19 | filed | Bug in autocti_workspace: the dataset_1d results/database example… | +| 2026-08-18 | parked | single-source-density-design | | 2026-08-18 | parked | prior-message-collapse-design | | 2026-08-18 | filed | @PyAutoFit TransformedMessage.logpdf/pdf omit the transform… | | 2026-08-17 | filed | Which other searches need prior-support handling — coverage audit… | | 2026-08-17 | filed | Teach repos_sync --write to stamp organ config surfaces | | 2026-08-14 | filed | Three jax_likelihood pins are stale by ~1.24e-4 and fail the smoke… | | 2026-08-09 | found | isothermal-ell-sph-oversampling-at-the-cusp | -| 2026-08-08 | parked | pyautoreduce-slacs1430-acs-comparison | -| 2026-08-08 | filed | Regenerate autolens_workspace markdown/ so the MGE pages show… | -| 2026-08-07 | filed | Regenerate setup_notebook-drifted notebooks in… | -| 2026-08-06 | filed | Triage: Convolver "No blurring_image provided" warning in canonical… |
… 10 more (10 left) | Date | Event | Task | |------|-------|------| +| 2026-08-08 | parked | pyautoreduce-slacs1430-acs-comparison | +| 2026-08-08 | filed | Regenerate autolens_workspace markdown/ so the MGE pages show… | +| 2026-08-07 | filed | Regenerate setup_notebook-drifted notebooks in… | +| 2026-08-06 | filed | Triage: Convolver "No blurring_image provided" warning in canonical… | | 2026-08-06 | filed | Rewrite PyAutoCTI docs/api — 55 of 89 autosummary entries are dead | | 2026-08-06 | filed | Dependency-cap refresh 2026-08: safe bumps, astropy 8 decision, two… | | 2026-08-05 | filed | Give PyAutoFit searches a seed — today no search can be made… | | 2026-08-04 | filed | dataset/imaging/jwst_lw is untracked because the gitignore was never… | | 2026-08-04 | filed | cosmos_web_ring stores boolean masks as float64, wasting ~3.4 MB of… | | 2026-08-04 | filed | autolens_workspace_developer: broad stale-API rot (56 symbols, no CI) | -| 2026-08-04 | filed | Nightly release has been blocked 8 nights running — triage the streak | -| 2026-08-04 | filed | HowToLens ch4 tutorial 3: mask overlay is never actually drawn | -| 2026-08-03 | filed | Un-park imaging/features/scaling_relation/slam — the PyAutoArray#431… | -| 2026-07-31 | filed | interferometer/start_here.py OOM in nightly release-validation… |
diff --git a/draft/refactor/autofit/optimisation_state_limit_guard_truthiness.md b/draft/refactor/autofit/optimisation_state_limit_guard_truthiness.md new file mode 100644 index 00000000..843f70a7 --- /dev/null +++ b/draft/refactor/autofit/optimisation_state_limit_guard_truthiness.md @@ -0,0 +1,86 @@ +# `OptimisationState.valid` guards on dict truthiness, not on "is a limit set" + +Type: refactor +Target: autofit +Repos: +- PyAutoFit +Difficulty: small +Autonomy: safe +Priority: low +Status: formalised +Filed: 2026-08-27 + +Follow-up owed by `complete/2026/08/loggaussian-prior-declares-own-support.md` +(PyAutoFit#1526 / #1527), which listed it as an open item. + +## Correction to the inherited claim — read this first + +PyAutoFit#1527's own "Deliberately not done" list says: + +> `line_search.OptimisationState.valid` uses `if self.lower_limit and …`, which +> is falsy at `0.0`. + +**That characterisation is wrong, and the prompt would be a bug report if it were +right.** `self.lower_limit` is not a scalar. It is a `VariableData` +(`autofit/mapper/variable.py:309`, a `Dict[Variable, np.ndarray]` subclass), +built by `MeanField.lower_limit` (`autofit/graphical/mean_field.py:259`) and +passed in by `LaplaceOptimiser` only when `check_limits=True` +(`autofit/graphical/laplace/optimiser.py:96-99`). Dict truthiness is +non-emptiness, so the guard is `False` only for a model with **no free +variables**, where the check is vacuous anyway. There is no `0.0` for it to be +falsy at, and no prior — LogGaussian included — can make it one. + +Worth stating explicitly: the EP/Laplace path reads `m.lower_limit` off the +**message**, and #1527 deliberately left `message.lower_limit` at `±inf`. So +this code sees exactly what it saw before that fix. Nothing about it changed. + +## What is actually here + +`autofit/graphical/laplace/line_search.py:107-114`: + +```python +@property +def valid(self): + if self.lower_limit and (self.parameters < self.lower_limit).any(): + return False + + if self.upper_limit and (self.parameters > self.upper_limit).any(): + return False + + return True +``` + +Both attributes default to `None` (`line_search.py:78-79`), so the *intent* of +the guard is plainly "was a limit supplied?" — and `if x` happens to express +that correctly today, by accident of `VariableData` being a dict. It is a +readability and robustness defect, not a live bug: + +- It reads as a scalar test to anyone skimming it, which is exactly how it got + written into #1527's follow-up list as a `0.0` bug. +- It is one type change away from being a real one. If `lower_limit` ever + becomes an array, `if self.lower_limit` raises + `ValueError: truth value of an array with more than one element is ambiguous`; + if it becomes a scalar, the `0.0` bug #1527 described becomes real. + +## The fix + +Replace both guards with `is not None`, and add a short comment saying the +attributes are `VariableData` keyed by free variable, `None` when +`check_limits=False`. + +## Verify + +- `valid` returns the same answer for every case the suite already covers — + this is behaviour-preserving by construction, since the only truthiness case + that differs is the empty `VariableData`, where both comparison expressions + are vacuously `False` and the property returns `True` either way. +- A `LaplaceOptimiser(check_limits=True)` EP run is unchanged. +- Confirm no other call site constructs `OptimisationState` with a scalar or + bare-array `lower_limit` (`grep -rn "lower_limit" autofit/graphical/` had one + producer at the time of filing, `optimiser.py:98`). + +## Scope note + +Do **not** widen this into "make EP read the prior's declared support instead of +the message's". That is the separate, deliberately-rejected question in +`draft/research/graphical_ep/transformed_message_declares_support.md`. diff --git a/draft/refactor/autofit/redundant_prior_limits_overrides.md b/draft/refactor/autofit/redundant_prior_limits_overrides.md new file mode 100644 index 00000000..e2f5a91f --- /dev/null +++ b/draft/refactor/autofit/redundant_prior_limits_overrides.md @@ -0,0 +1,68 @@ +# Three `limits` overrides are now exact duplicates of `Prior.limits` + +Type: refactor +Target: autofit +Repos: +- PyAutoFit +Difficulty: small +Autonomy: safe +Priority: low +Status: formalised +Filed: 2026-08-27 + +Follow-up owed by `complete/2026/08/loggaussian-prior-declares-own-support.md` +(PyAutoFit#1526 / #1527), which named it and deliberately left it out to keep +that PR narrow. + +## The duplication + +PyAutoFit#1527 changed `Prior.limits` (`autofit/mapper/prior/abstract.py:360-373`) +from a hardcoded `(float("-inf"), float("inf"))` to + +```python +return (float(self.lower_limit), float(self.upper_limit)) +``` + +Three subclasses override it with what is now the same thing: + +| File | Line | Body | +|---|---|---| +| `autofit/mapper/prior/uniform.py` | 205 | `return self.lower_limit, self.upper_limit` | +| `autofit/mapper/prior/log_uniform.py` | 199 | `return self.lower_limit, self.upper_limit` | +| `autofit/mapper/prior/truncated_gaussian.py` | 122 | `return self.lower_limit, self.upper_limit` | + +## The one difference, and it is not cosmetic + +The base coerces with `float(...)`; the three overrides do not. So deleting them +changes the *type* of what `limits` returns for these priors — a bare attribute +(possibly a numpy scalar, or a JAX tracer under `jit`) becomes a Python `float`. + +That is the whole risk of this task, and it is why it is not a pure deletion: + +- `AbstractPriorModel.mapper_from_prior_means` feeds `limits` straight into + `TruncatedGaussianPrior(mean, sigma, *limits)` + (`autofit/mapper/prior_model/abstract.py:1147-1156`), so the constructed + prior's limit types change with it. +- Anything under `jax.jit` that reaches `limits` on a traced value would go from + passing the tracer through to raising `ConcretizationTypeError` at `float()`. + +Check both before deleting. If the coercion turns out to be the problem rather +than the overrides, the right change may be the reverse — drop `float()` from +the base — in which case say so and stop. + +## The fix + +Delete the three overrides if and only if the type question above comes back +clean. Otherwise reconcile the base and the overrides on one convention and +document which. + +## Verify + +- `UniformPrior(0.0, 2.0).limits == (0.0, 2.0)`, and likewise for `LogUniform` + and `TruncatedGaussian`, before and after — values *and* types. +- `test_autofit/mapper/prior/test_prior_properties.py` P6 (the reported-support + property added by #1527) still passes for every prior family. +- A JAX-path fit that touches `limits` under `jit`, if one exists — if none + does, say so rather than assuming the path is unreachable. #1477's process + lesson applies: the 1790-test suite passed against an `LBFGS._fit` that raised + `NameError` on every real call, because nothing executed it. diff --git a/draft/research/graphical_ep/transformed_message_declares_support.md b/draft/research/graphical_ep/transformed_message_declares_support.md new file mode 100644 index 00000000..e180088d --- /dev/null +++ b/draft/research/graphical_ep/transformed_message_declares_support.md @@ -0,0 +1,65 @@ +# Should `TransformedMessage` carry its own support, rather than the prior? + +Type: research +Target: graphical_ep +Repos: +- PyAutoFit +Difficulty: medium +Autonomy: human-required +Priority: low +Status: formalised +Filed: 2026-08-27 + +Follow-up owed by `complete/2026/08/loggaussian-prior-declares-own-support.md` +(PyAutoFit#1526 / #1527), which **rejected** this route and shadowed the limits +on the prior instead. This prompt is the open question, not a plan to reverse +that decision. + +## Why #1527 did not do it + +`LogGaussianPrior` reported `(-inf, inf)` because its `TransformedMessage` +defaults its limits to `±inf` and was never passed any. The obvious fix — pass +the real limits into the message — was rejected for two measured reasons: + +1. **They do not survive the message lifecycle.** Limits set on the message are + dropped by `with_base`, `copy`, `project` and `__call__`. A prior that + declared its support that way would lose it on the first projection. +2. **It is a live EP behaviour change.** `MeanField.lower_limit` + (`autofit/graphical/mean_field.py:259`) reads `m.lower_limit` off each + message and hands it to `OptimisationState.valid` via + `LaplaceOptimiser(check_limits=True)` + (`autofit/graphical/laplace/optimiser.py:96-99`). Giving a `TransformedMessage` + a real `(0, inf)` support would start rejecting EP/Laplace states that are + accepted today — well outside a bug fix, and unmeasured. + +So #1527 set `self.lower_limit = 0.0` on the prior instance and left +`message.lower_limit` at `±inf`. #1527 verified that choice keeps EP/Laplace +seeing exactly what it saw before. + +## The question this leaves open + +The organism now has **two notions of support that deliberately disagree**: the +prior says `(0, inf)`, its message says `(-inf, inf)`. That is a correct +resolution of a scope problem, not a correct end state — it is the same shape of +divergence as the `Prior.limits`-vs-`lower_limit` split that #1527 closed, one +layer down. + +Worth answering, in this order: + +1. **Is `check_limits=True` EP actually wrong today** for a model carrying a + `LogGaussianPrior`? It accepts states with a negative value for a strictly + positive parameter. Does that produce a bad fit, or does the factor's own + `-inf` density reject it downstream anyway? Measure before designing. +2. If it is wrong, **which layer should own the support** — the message (fixing + the lifecycle drops in `with_base` / `copy` / `project` / `__call__` first), + or `MeanField.lower_limit` reading through to the prior? +3. What is the EP regression surface? Any change here re-keys nothing but can + change which states `OptimisationState.valid` rejects, and therefore where a + Laplace optimisation lands. + +## Related + +- `active/13_collapse_prior_and_message.md` — the standing task on the + Prior/Message split this sits inside. Read it first; this may be a sub-question + of it rather than its own task. +- `complete/2026/08/transformed-message-semantics-doc.md`. diff --git a/draft/test/autogalaxy/prior_passing_loggaussian_lower_bound.md b/draft/test/autogalaxy/prior_passing_loggaussian_lower_bound.md new file mode 100644 index 00000000..7574724a --- /dev/null +++ b/draft/test/autogalaxy/prior_passing_loggaussian_lower_bound.md @@ -0,0 +1,70 @@ +# Spot-check downstream prior passing after `LogGaussianPrior` gained a `0.0` lower limit + +Type: test +Target: autogalaxy +Repos: +- PyAutoGalaxy +- PyAutoLens +Difficulty: small +Autonomy: supervised +Priority: normal +Status: formalised +Filed: 2026-08-27 + +Follow-up owed by `complete/2026/08/loggaussian-prior-declares-own-support.md` +(PyAutoFit#1526 / #1527). That PR named the spot-check and **did not do it**. + +## The change that reaches downstream + +PyAutoFit#1527 is behaviour-preserving everywhere except one path, which it +measured and flagged. `AbstractPriorModel.mapper_from_prior_means` falls back to +`prior.limits` when the priors config supplies no `Limits` entry for a parameter +(`autofit/mapper/prior_model/abstract.py:1147-1156`), then constructs +`TruncatedGaussianPrior(mean, sigma, *limits)`. + +For a LogGaussian parameter with no config `Limits` entry: + +| | before #1527 | after #1527 | +|---|---|---| +| passed prior | `TruncatedGaussian(1.0, 0.5, -inf, inf)` | `TruncatedGaussian(1.0, 0.5, 0.0, inf)` | +| `value_for(0.001)` | **`-0.545`** | `0.0089` | +| `log_prior_from_value(-1.0)` | **`-8.0`** (finite) | `-inf` | + +The old behaviour was a strictly positive parameter being passed a prior that +samples negative values, so this is a correctness fix. But it **changes the +unit-cube mapping of the passed prior**, and a changed unit-cube mapping is a +changed inter-phase result. + +Note what is *not* at risk, so the check stays scoped: identifiers are +byte-identical (`__identifier_fields__ = ("mean", "sigma")` gates them), the +density is pointwise identical, and the direct (non-passed) unit-cube mapping is +identical over a 10-point grid. #1527 measured all three. Only **prior passing** +moved. + +## What to check + +1. **Does any PyAutoGalaxy / PyAutoLens model actually use `LogGaussianPrior`?** + Grep the config trees (`config/priors/**`) and the pipeline/search-chaining + code. If the answer is no, the task is done — record that and retire it. Do + not skip this step; it is most of the value here. +2. If yes, **does the priors config supply a `Limits` entry** for those + parameters? A config `Limits` entry wins over `prior.limits`, so a configured + parameter is unaffected. Only the unconfigured ones changed. +3. For any parameter that reaches the fallback, confirm the new lower-bounded + passed prior is what the science wants — it should be, since the parameter is + strictly positive, but confirm rather than assume. + +## Verify + +- An inter-phase prior-passing run (a search-chaining example touching a + LogGaussian parameter) completes and produces a passed prior whose + `lower_limit` is `0.0`, with no negative draws. +- State explicitly in the completion record which repos, configs and parameters + were checked, and whether any were affected — a null result here is the useful + answer and should be recorded as one, not left implicit. + +## Environment note + +Neither repo was attached when this prompt was filed; the grounding above is from +PyAutoFit `main` and from PyAutoFit#1527's measurements. Attach PyAutoGalaxy and +PyAutoLens before starting. From 442a67ec5c7cdcb2841d0482af277080d5a8dd39 Mon Sep 17 00:00:00 2001 From: Claude Date: Thu, 27 Aug 2026 17:13:43 +0000 Subject: [PATCH 3/4] prompt: work the four PyAutoFit#1527 follow-ups MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 1 and 2 implemented together on PyAutoFit claude/loggaussian-prior-support-ngh59x (4c0f79b); status appended to both prompts, which stay in draft/ until a PR is opened. - Doing 1 exposed a live bug: VariableData.any reduced through var_all, so OptimisationState.valid reported a parameter vector valid when only SOME components were outside their limits. Fixed in the same commit. - 3 partly answered and the measurements appended: EP's check_limits does not enforce LogGaussian's support, but the message's own density returns a clean -inf, so nothing is wrong today. Design question stays open, lower priority. - 4 done, null result, retired to complete/2026/08/ — neither PyAutoGalaxy nor PyAutoLens constructs a LogGaussianPrior, so there is no downstream exposure. Dashboard regenerated; lifecycle.py check OK. Co-Authored-By: Claude --- .../loggaussian-prior-declares-own-support.md | 23 ++- .../prior-passing-loggaussian-lower-bound.md | 162 ++++++++++++++++++ complete/index.md | 3 +- dashboard.html | 21 ++- dashboard.md | 24 +-- ...timisation_state_limit_guard_truthiness.md | 25 +++ .../redundant_prior_limits_overrides.md | 25 +++ .../transformed_message_declares_support.md | 47 +++++ .../prior_passing_loggaussian_lower_bound.md | 70 -------- 9 files changed, 301 insertions(+), 99 deletions(-) create mode 100644 complete/2026/08/prior-passing-loggaussian-lower-bound.md delete mode 100644 draft/test/autogalaxy/prior_passing_loggaussian_lower_bound.md diff --git a/complete/2026/08/loggaussian-prior-declares-own-support.md b/complete/2026/08/loggaussian-prior-declares-own-support.md index cd0318a5..928e9d2d 100644 --- a/complete/2026/08/loggaussian-prior-declares-own-support.md +++ b/complete/2026/08/loggaussian-prior-declares-own-support.md @@ -126,7 +126,28 @@ part of #1527. 4. **Downstream prior-passing spot-check** in PyAutoGalaxy / PyAutoLens, per the section above. Filed as `draft/test/autogalaxy/prior_passing_loggaussian_lower_bound.md`. -All four were filed as prompts on 2026-08-27, alongside this record. +All four were filed as prompts on 2026-08-27, alongside this record, and worked the +same day: + +- **1 and 2 implemented together** on PyAutoFit `claude/loggaussian-prior-support-ngh59x` + (`4c0f79b`, suite 2186/36 vs baseline 2178/36). No PR opened yet. +- **Doing 1 exposed a live bug neither #1527 nor the prompt saw.** `VariableData.any` + reduced through `var_all`, so it meant "is there a variable whose elements are ALL + True" rather than "is ANY element True". `OptimisationState.valid` asks + `(parameters < lower_limit).any()` — so a parameter vector with *some* components + outside their limits was reported **valid**, and `MeanField`'s `valid.any()` + under-reported the same way. Fixed in the same commit. That, not the truthiness + guard, is why the limits check under-enforced; it surfaced only because rewriting + the guard needed a test and `OptimisationState.valid` had **no coverage at all** — + the #1477 process lesson, third time it has paid out in this lineage. +- **3 partly answered.** EP's `check_limits` path genuinely does not enforce + LogGaussian's support (it reads the message's `-inf`), but the message's own density + returns a clean `-inf` at negative values — no `NaN` — so EP is not producing wrong + results today. The check is redundant for this prior, not load-bearing. Measurements + appended to the prompt; the design question stays open at lower priority. +- **4 done, null result** — `complete/2026/08/prior-passing-loggaussian-lower-bound.md`. + Neither PyAutoGalaxy nor PyAutoLens constructs a `LogGaussianPrior`, so the + prior-passing change has no downstream exposure. ## Repos / worktree diff --git a/complete/2026/08/prior-passing-loggaussian-lower-bound.md b/complete/2026/08/prior-passing-loggaussian-lower-bound.md new file mode 100644 index 00000000..96d0a3c7 --- /dev/null +++ b/complete/2026/08/prior-passing-loggaussian-lower-bound.md @@ -0,0 +1,162 @@ +- issue: none — investigated and closed without one +- completed: 2026-08-27 +- pr: none; **no code change was required** +- summary: NULL RESULT. Spot-check owed by PyAutoFit#1527, which changed the + prior-passing fallback for `LogGaussianPrior` and flagged PyAutoGalaxy / + PyAutoLens as worth checking. Neither repo constructs a `LogGaussianPrior` + anywhere, so there is no exposure. Filed and closed the same day. +- validation: grep over fresh shallow clones (PyAutoGalaxy `05e5d13`, PyAutoLens + same date) across `*.py`, `*.yaml`, `*.yml`, `*.json` and every other tracked + file type. + +## What was checked, and what it found + +PyAutoFit#1527 made `LogGaussianPrior` report `(0.0, inf)` instead of +`(-inf, inf)`. That is downstream-visible in exactly one place: +`AbstractPriorModel.mapper_from_prior_means` falls back to `prior.limits` when +the priors config has no `Limits` entry for a parameter +(`autofit/mapper/prior_model/abstract.py:1153`), then builds +`TruncatedGaussianPrior(mean, sigma, *limits)`. Before the fix that produced a +prior which samples negative values for a strictly positive parameter; after it, +a lower-bounded one — a correctness fix that nonetheless **changes the unit-cube +mapping** of the passed prior. + +The only occurrence of the name in either repo is an API-docs autosummary entry: + +- `pyautogalaxy/docs/api/modeling.rst:57` +- `pyautolens/docs/api/modeling.rst:56` + +Zero hits in source, zero in any priors config. Configured prior families: + +| repo | configured `type:` values | +|---|---| +| PyAutoGalaxy | Absolute 420, Relative 277, Uniform 268, Gaussian 238, TruncatedGaussian 96, LogUniform 93, Constant 42 | +| PyAutoLens | Absolute 123, Uniform 101, Relative 73, Gaussian 62, TruncatedGaussian 26, LogUniform 23 | + +`Absolute` / `Relative` are width modifiers, not prior families. + +## Why that is conclusive rather than suggestive + +`LogGaussianPrior` is the **only** prior whose `limits` #1527 changed. `Uniform`, +`LogUniform` and `TruncatedGaussian` already reported their real bounds and were +measured byte-identical across the change; `Gaussian` still reports +`(-inf, inf)`. So the fallback cannot return a changed value for any prior either +repo actually uses. No inter-phase prior-passing result moves. + +## When to re-run this + +Only if either repo starts using `LogGaussianPrior`. At that point the parameter +either gets a `Limits` entry in the priors config (which wins over +`prior.limits`, so nothing changes) or lands on the now-correct lower-bounded +fallback — which is the desired behaviour, not a regression. + +## Note on the record + +A null result recorded explicitly is the deliverable here. The alternative — a +prompt quietly retired because "nothing was found" — leaves the next reader +unable to tell a completed check from an abandoned one. + +## Original prompt + +# Spot-check downstream prior passing after `LogGaussianPrior` gained a `0.0` lower limit + +Type: test +Target: autogalaxy +Repos: +- PyAutoGalaxy +- PyAutoLens +Difficulty: small +Autonomy: supervised +Priority: normal +Status: formalised +Filed: 2026-08-27 + +Follow-up owed by `complete/2026/08/loggaussian-prior-declares-own-support.md` +(PyAutoFit#1526 / #1527). That PR named the spot-check and **did not do it**. + +## The change that reaches downstream + +PyAutoFit#1527 is behaviour-preserving everywhere except one path, which it +measured and flagged. `AbstractPriorModel.mapper_from_prior_means` falls back to +`prior.limits` when the priors config supplies no `Limits` entry for a parameter +(`autofit/mapper/prior_model/abstract.py:1147-1156`), then constructs +`TruncatedGaussianPrior(mean, sigma, *limits)`. + +For a LogGaussian parameter with no config `Limits` entry: + +| | before #1527 | after #1527 | +|---|---|---| +| passed prior | `TruncatedGaussian(1.0, 0.5, -inf, inf)` | `TruncatedGaussian(1.0, 0.5, 0.0, inf)` | +| `value_for(0.001)` | **`-0.545`** | `0.0089` | +| `log_prior_from_value(-1.0)` | **`-8.0`** (finite) | `-inf` | + +The old behaviour was a strictly positive parameter being passed a prior that +samples negative values, so this is a correctness fix. But it **changes the +unit-cube mapping of the passed prior**, and a changed unit-cube mapping is a +changed inter-phase result. + +Note what is *not* at risk, so the check stays scoped: identifiers are +byte-identical (`__identifier_fields__ = ("mean", "sigma")` gates them), the +density is pointwise identical, and the direct (non-passed) unit-cube mapping is +identical over a 10-point grid. #1527 measured all three. Only **prior passing** +moved. + +## What to check + +1. **Does any PyAutoGalaxy / PyAutoLens model actually use `LogGaussianPrior`?** + Grep the config trees (`config/priors/**`) and the pipeline/search-chaining + code. If the answer is no, the task is done — record that and retire it. Do + not skip this step; it is most of the value here. +2. If yes, **does the priors config supply a `Limits` entry** for those + parameters? A config `Limits` entry wins over `prior.limits`, so a configured + parameter is unaffected. Only the unconfigured ones changed. +3. For any parameter that reaches the fallback, confirm the new lower-bounded + passed prior is what the science wants — it should be, since the parameter is + strictly positive, but confirm rather than assume. + +## Verify + +- An inter-phase prior-passing run (a search-chaining example touching a + LogGaussian parameter) completes and produces a passed prior whose + `lower_limit` is `0.0`, with no negative draws. +- State explicitly in the completion record which repos, configs and parameters + were checked, and whether any were affected — a null result here is the useful + answer and should be recorded as one, not left implicit. + +## Environment note + +Neither repo was attached when this prompt was filed; the grounding above is from +PyAutoFit `main` and from PyAutoFit#1527's measurements. Attach PyAutoGalaxy and +PyAutoLens before starting. + +## Result 2026-08-27 — NULL RESULT, no exposure + +Checked against fresh shallow clones of both repos +(PyAutoGalaxy `05e5d13`, PyAutoLens at the same date). + +**Neither repo constructs a `LogGaussianPrior` anywhere.** The only occurrence of +the name in either tree is an autosummary listing in the API docs: + +- `pyautogalaxy/docs/api/modeling.rst:57` +- `pyautolens/docs/api/modeling.rst:56` + +Zero hits in source (`--include=*.py`) and zero in any priors config +(`*.yaml` / `*.yml` / `*.json`). The prior types actually configured are: + +| repo | configured `type:` values | +|---|---| +| PyAutoGalaxy | Absolute 420, Relative 277, Uniform 268, Gaussian 238, TruncatedGaussian 96, LogUniform 93, Constant 42 | +| PyAutoLens | Absolute 123, Uniform 101, Relative 73, Gaussian 62, TruncatedGaussian 26, LogUniform 23 | + +(`Absolute` / `Relative` are width modifiers, not prior families.) + +`LogGaussianPrior` is the **only** prior whose `limits` PyAutoFit#1527 changed — +`Uniform`, `LogUniform` and `TruncatedGaussian` were already truthful and +`Gaussian` still reports `(-inf, inf)`. So the prior-passing fallback at +`autofit/mapper/prior_model/abstract.py:1153` cannot reach a changed value from +either repo, and there is no inter-phase prior-passing exposure to check. + +**This task is done. Retire it.** The check is worth re-running only if either +repo starts using `LogGaussianPrior` — at which point the parameter would need a +`Limits` entry in the priors config, or it lands on the (now correct) +lower-bounded fallback. diff --git a/complete/index.md b/complete/index.md index 3d921b17..2515526e 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. -1154 records across 7 buckets. +1155 records across 7 buckets. ## Highlights @@ -189,6 +189,7 @@ _(curate hard-won records here — survives regeneration.)_ - [power-law-omega-convergence](2026/08/power-law-omega-convergence.md) — Bounded the fixed 20-term JAX PowerLaw omega recurrence across the packaged slope and ellipticity priors, meas… - [pr-ci-for-own-test-suite](2026/08/pr-ci-for-own-test-suite.md) - [pre-build-stages-untracked-wip](2026/08/pre-build-stages-untracked-wip.md) — CLOSED completed +- [prior-passing-loggaussian-lower-bound](2026/08/prior-passing-loggaussian-lower-bound.md) — NULL RESULT. Spot-check owed by PyAutoFit#1527, which changed the - [prior-property-tests](2026/08/prior-property-tests.md) - [prior-support-clipper](2026/08/prior-support-clipper.md) — Shipped `AbstractClipper` / `ClipperNone` / `ClipperPriorBox` in - [priors-and-messages-math-audit](2026/08/priors-and-messages-math-audit.md) diff --git a/dashboard.html b/dashboard.html index 99c2128f..85f5f2c6 100644 --- a/dashboard.html +++ b/dashboard.html @@ -204,7 +204,7 @@

PyAutoMindDashboard

Intent. Priority. Flow.

Every task the Mind is holding. Tap a task's 📋 and its /start_dev command is on your clipboard — paste it into a Claude Code chat to route Claude straight to that task. Recent is the same work by date — what has been happening rather than what to do next.

-
  • 1In flight
  • 3Parked
  • 5Planned
  • 139Backlog
+
  • 1In flight
  • 3Parked
  • 5Planned
  • 138Backlog

Last updated 2026-08-27. This page is generated from active/, draft/ and the registry files, so it is only as current as they are. dashboard_refresh.yml re-renders it on every push to main — that heals a stale page, but not a stale prompt: a task that shipped without its prompt advancing to complete/ keeps rendering here as pickable backlog. Reconciling those is the refresh below.

latent-nan-guard-honest-run — planned 2026-07-22

Backlog markdown version

-

139 filed prompts, not started — sorted most-pickable first (priority, then size). 25 of them belong to an epic and are listed only under Epics below.

+

138 filed prompts, not started — sorted most-pickable first (priority, then size). 25 of them belong to an epic and are listed only under Epics below.

feature — 28 @@ -391,11 +391,10 @@

Backlog

Deduplicate repos_sync.py's check/write pairs♻️ refactorpyautomindmediumsafelow

-test — 7 +test — 6

Cross-validate multi-plane ray tracing🧪 testautolenslargesupervisedhigh

Re-baseline the MGE imaging JIT profiling regression value🧪 testautolens_workspace_developertoo-largesupervisedhigh

-

Relevance-gate the reusable smoke workflow so a PR only runs…🧪 testpyautoheartmediumsupervisednormal

@@ -430,12 +429,6 @@

Backlog 2026-08-27 filed -Spot-check downstream prior passing after LogGaussianPrior gained a… - - - -2026-08-27 -filed Should TransformedMessage carry its own support, rather than the… @@ -475,7 +468,7 @@

Backlog The Brain board should work in a session that has no gh - + 2026-08-24 filed wiki-currency's --check-version gate rots on every library main merge @@ -715,6 +708,12 @@

Backlog autolens_workspace_developer: broad stale-API rot (56 symbols, no CI) + +2026-08-04 +filed +Nightly release has been blocked 8 nights running — triage the streak + +

Epics markdown version

diff --git a/dashboard.md b/dashboard.md index b37f04f5..4c873711 100644 --- a/dashboard.md +++ b/dashboard.md @@ -45,7 +45,7 @@ anything you could not verify. | [In flight](#in-flight) (`active/`) | 1 | | [Parked](#parked) (`parked.md`) | 3 | | [Planned](#planned) (`planned.md`) | 5 | -| [Backlog](#backlog) (`draft/`) | 139 | +| [Backlog](#backlog) (`draft/`) | 138 | ## Start here @@ -261,7 +261,7 @@ Scoped but not started; some are not yet prompt files. Full detail in [`planned. ## Backlog -**139** filed prompts, not started. Each section is sorted most-pickable first (priority, then size). **25** of them belong to an epic and are listed only under [Epics](#epics) below. +**138** filed prompts, not started. Each section is sorted most-pickable first (priority, then size). **25** of them belong to an epic and are listed only under [Epics](#epics) below.
feature — 28 @@ -1102,7 +1102,7 @@ Scoped but not started; some are not yet prompt files. Full detail in [`planned.
-test — 7 +test — 6
📋 Cross-validate multi-plane ray tracing — autolens · large · supervised · high @@ -1128,14 +1128,6 @@ Scoped but not started; some are not yet prompt files. Full detail in [`planned.
-
📋 Spot-check downstream prior passing after LogGaussianPrior gained a 0.0 lower limit — autogalaxy · small · supervised · normal - -``` -/start_dev draft/test/autogalaxy/prior_passing_loggaussian_lower_bound.md -``` - -
-
📋 Relevance-gate the reusable smoke workflow so a PR only runs… — pyautoheart · medium · supervised · normal ``` @@ -1228,7 +1220,6 @@ The 50 newest things to happen to the work in hand, newest first — issued, par |------|-------|------| | 2026-08-27 | filed | OptimisationState.valid guards on dict truthiness, not on "is a… | | 2026-08-27 | filed | Three limits overrides are now exact duplicates of Prior.limits | -| 2026-08-27 | filed | Spot-check downstream prior passing after LogGaussianPrior gained a… | | 2026-08-27 | filed | Should TransformedMessage carry its own support, rather than the… | | 2026-08-27 | filed | One construction path for plane-bound lensing quantities | | 2026-08-27 | filed | Multi-plane time delays | @@ -1236,12 +1227,12 @@ The 50 newest things to happen to the work in hand, newest first — issued, par | 2026-08-27 | filed | Cross-validate multi-plane ray tracing | | 2026-08-26 | filed | jax_profiling/gradient/imaging/pixelization.py: 3.2% of its pin move… | | 2026-08-26 | filed | The Brain board should work in a session that has no gh | +| 2026-08-24 | filed | wiki-currency's --check-version gate rots on every library main merge |
… 10 more (40 left) | Date | Event | Task | |------|-------|------| -| 2026-08-24 | filed | wiki-currency's --check-version gate rots on every library main merge | | 2026-08-24 | filed | interferometer/jax_grad/gradient.py: eager and jitted likelihoods… | | 2026-08-24 | filed | autocti_workspace has no Navigator Check, so its CI can never roll… | | 2026-08-24 | filed | Un-park multi_galaxy/features/scaling_relation/slam once a capped run… | @@ -1251,12 +1242,12 @@ The 50 newest things to happen to the work in hand, newest first — issued, par | 2026-08-23 | filed | Phase 3: stop installing pynufft in Hands/Heart CI and PyAutoCTI… | | 2026-08-23 | filed | Brain board follow-ups: what real mornings surface | | 2026-08-22 | filed | smoke_install.sh's stale jax<0.7 pin — CI is on the right jax… | +| 2026-08-22 | filed | Untrack the generated FITS test artifacts in autoarray |
… 10 more (30 left) | Date | Event | Task | |------|-------|------| -| 2026-08-22 | filed | Untrack the generated FITS test artifacts in autoarray | | 2026-08-22 | filed | The reconstruction noise map describes a different estimator than the… | | 2026-08-22 | filed | Point-source JSON datasets record no resolution regime | | 2026-08-22 | filed | Is Intel macOS a supported platform, and what is the numpy-only… | @@ -1266,12 +1257,12 @@ The 50 newest things to happen to the work in hand, newest first — issued, par | 2026-08-19 | filed | autofit_profiling: bootstrap the repo + general PyAutoFit profiling… | | 2026-08-19 | issued | @PyAutoFit TransformedMessage.factor_gradient crashes on first… | | 2026-08-19 | filed | Release board: local run_logs enrichment | +| 2026-08-19 | filed | RTD organism docs currency: Nerves page, organ-count drift, hands.md… |
… 10 more (20 left) | Date | Event | Task | |------|-------|------| -| 2026-08-19 | filed | RTD organism docs currency: Nerves page, organ-count drift, hands.md… | | 2026-08-19 | filed | Deduplicate repos_sync.py's check/write pairs | | 2026-08-19 | filed | Bug in autocti_workspace: the dataset_1d results/database example… | | 2026-08-18 | parked | single-source-density-design | @@ -1281,12 +1272,12 @@ The 50 newest things to happen to the work in hand, newest first — issued, par | 2026-08-17 | filed | Teach repos_sync --write to stamp organ config surfaces | | 2026-08-14 | filed | Three jax_likelihood pins are stale by ~1.24e-4 and fail the smoke… | | 2026-08-09 | found | isothermal-ell-sph-oversampling-at-the-cusp | +| 2026-08-08 | parked | pyautoreduce-slacs1430-acs-comparison |
… 10 more (10 left) | Date | Event | Task | |------|-------|------| -| 2026-08-08 | parked | pyautoreduce-slacs1430-acs-comparison | | 2026-08-08 | filed | Regenerate autolens_workspace markdown/ so the MGE pages show… | | 2026-08-07 | filed | Regenerate setup_notebook-drifted notebooks in… | | 2026-08-06 | filed | Triage: Convolver "No blurring_image provided" warning in canonical… | @@ -1296,6 +1287,7 @@ The 50 newest things to happen to the work in hand, newest first — issued, par | 2026-08-04 | filed | dataset/imaging/jwst_lw is untracked because the gitignore was never… | | 2026-08-04 | filed | cosmos_web_ring stores boolean masks as float64, wasting ~3.4 MB of… | | 2026-08-04 | filed | autolens_workspace_developer: broad stale-API rot (56 symbols, no CI) | +| 2026-08-04 | filed | Nightly release has been blocked 8 nights running — triage the streak |
diff --git a/draft/refactor/autofit/optimisation_state_limit_guard_truthiness.md b/draft/refactor/autofit/optimisation_state_limit_guard_truthiness.md index 843f70a7..3cc64190 100644 --- a/draft/refactor/autofit/optimisation_state_limit_guard_truthiness.md +++ b/draft/refactor/autofit/optimisation_state_limit_guard_truthiness.md @@ -84,3 +84,28 @@ attributes are `VariableData` keyed by free variable, `None` when Do **not** widen this into "make EP read the prior's declared support instead of the message's". That is the separate, deliberately-rejected question in `draft/research/graphical_ep/transformed_message_declares_support.md`. + +## Status 2026-08-27 — implemented, pushed, not yet PR'd + +Both this and its sibling `redundant_prior_limits_overrides.md` were implemented +together on PyAutoFit branch `claude/loggaussian-prior-support-ngh59x`, commit +`4c0f79b` — one coherent change (the limits cleanup #1527 left behind) rather +than two PRs, which departs from "one prompt = one task = one PR" deliberately +and is worth splitting if a reviewer prefers. + +Full suite **2186 passed / 36 skipped** (baseline 2178/36, +8 new tests). +No PR opened. + +**A live bug turned up inside this work.** `VariableData.any` reduced through +`var_all`, so it answered "is there a variable whose elements are ALL True" +rather than "is ANY element True". `OptimisationState.valid` asks +`(parameters < lower_limit).any()`, so a parameter vector with *some* components +outside their limits was reported **valid** — only a variable violating on every +component was caught. `MeanField`'s `valid.any()` under-reported the same way. +Fixed in the same commit; the other four `.any()` call sites in the library are +numpy arrays and are untouched. + +That bug, not the truthiness guard, is why the limits check under-enforced. This +prompt's own framing (a readability defect) was right about the guard and missed +the real one underneath it — found only because the guard rewrite needed a test +and `OptimisationState.valid` had **no test coverage at all**. diff --git a/draft/refactor/autofit/redundant_prior_limits_overrides.md b/draft/refactor/autofit/redundant_prior_limits_overrides.md index e2f5a91f..68b0b24c 100644 --- a/draft/refactor/autofit/redundant_prior_limits_overrides.md +++ b/draft/refactor/autofit/redundant_prior_limits_overrides.md @@ -66,3 +66,28 @@ document which. does, say so rather than assuming the path is unreachable. #1477's process lesson applies: the 1790-test suite passed against an `LBFGS._fit` that raised `NameError` on every real call, because nothing executed it. + +## Status 2026-08-27 — implemented, pushed, not yet PR'd + +Both this and its sibling `redundant_prior_limits_overrides.md` were implemented +together on PyAutoFit branch `claude/loggaussian-prior-support-ngh59x`, commit +`4c0f79b` — one coherent change (the limits cleanup #1527 left behind) rather +than two PRs, which departs from "one prompt = one task = one PR" deliberately +and is worth splitting if a reviewer prefers. + +Full suite **2186 passed / 36 skipped** (baseline 2178/36, +8 new tests). +No PR opened. + +**A live bug turned up inside this work.** `VariableData.any` reduced through +`var_all`, so it answered "is there a variable whose elements are ALL True" +rather than "is ANY element True". `OptimisationState.valid` asks +`(parameters < lower_limit).any()`, so a parameter vector with *some* components +outside their limits was reported **valid** — only a variable violating on every +component was caught. `MeanField`'s `valid.any()` under-reported the same way. +Fixed in the same commit; the other four `.any()` call sites in the library are +numpy arrays and are untouched. + +That bug, not the truthiness guard, is why the limits check under-enforced. This +prompt's own framing (a readability defect) was right about the guard and missed +the real one underneath it — found only because the guard rewrite needed a test +and `OptimisationState.valid` had **no test coverage at all**. diff --git a/draft/research/graphical_ep/transformed_message_declares_support.md b/draft/research/graphical_ep/transformed_message_declares_support.md index e180088d..b69f0f0d 100644 --- a/draft/research/graphical_ep/transformed_message_declares_support.md +++ b/draft/research/graphical_ep/transformed_message_declares_support.md @@ -63,3 +63,50 @@ Worth answering, in this order: Prior/Message split this sits inside. Read it first; this may be a sub-question of it rather than its own task. - `complete/2026/08/transformed-message-semantics-doc.md`. + +## Measured 2026-08-27 — question 1 is answered: EP is NOT wrong today + +Run against a live 3.12 install on PyAutoFit `main`. The two layers do disagree, +exactly as described above: + +``` +prior.lower_limit = 0.0 (declared by #1527) +prior.limits = (0.0, inf) +message.lower_limit = -inf (deliberately left) +MeanField.lower_limit -> {LogGaussianPrior: -inf} # what OptimisationState gets +``` + +So `OptimisationState.valid` does **not** enforce LogGaussian's support: with +`params = -1.0`, `(params < MeanField.lower_limit).any()` is `False` and `valid` +returns `True`, for a value whose prior density is `-inf`. + +**But the message's own density already rejects it, cleanly:** + +| value | `message.logpdf` | `prior.log_prior_from_value` | +|---|---|---| +| `-1.0` | `-inf` | `-inf` | +| `-1e-9` | `-inf` | `-inf` | +| `0.0` | `-1.798e308` | `-inf` | +| `1e-9` | `-133.19` | `-111.29` | +| `1.0` | `-1.229` | `-0.047` | + +No `NaN` — which was the failure mode worth fearing, since a `log` of a negative +value could have produced one and poisoned the EP objective silently. It does +not. The limits check is **redundant for this prior, not load-bearing**, and EP +is not producing wrong results today because of the divergence. + +### What that does to this task + +It drops the priority. There is no live incorrectness to fix, so this stays what +the title says — a design question about which layer should own the support — +and not a bug. Two things are still worth someone's attention: + +- **`message.logpdf(0.0)` is `-1.798e308`, not `-inf`** (negative float max), + where the prior says `-inf`. Finite-but-astronomically-negative behaves + differently from `-inf` under any code that tests `isfinite`. Probably + harmless, unverified, and cheap to check. +- The redundancy is now *documented* rather than latent, which was most of the + hazard. Whoever picks this up starts from the table above. + +Remaining open: questions 2 and 3 (which layer should own it; the EP regression +surface). Question 1 is closed. diff --git a/draft/test/autogalaxy/prior_passing_loggaussian_lower_bound.md b/draft/test/autogalaxy/prior_passing_loggaussian_lower_bound.md deleted file mode 100644 index 7574724a..00000000 --- a/draft/test/autogalaxy/prior_passing_loggaussian_lower_bound.md +++ /dev/null @@ -1,70 +0,0 @@ -# Spot-check downstream prior passing after `LogGaussianPrior` gained a `0.0` lower limit - -Type: test -Target: autogalaxy -Repos: -- PyAutoGalaxy -- PyAutoLens -Difficulty: small -Autonomy: supervised -Priority: normal -Status: formalised -Filed: 2026-08-27 - -Follow-up owed by `complete/2026/08/loggaussian-prior-declares-own-support.md` -(PyAutoFit#1526 / #1527). That PR named the spot-check and **did not do it**. - -## The change that reaches downstream - -PyAutoFit#1527 is behaviour-preserving everywhere except one path, which it -measured and flagged. `AbstractPriorModel.mapper_from_prior_means` falls back to -`prior.limits` when the priors config supplies no `Limits` entry for a parameter -(`autofit/mapper/prior_model/abstract.py:1147-1156`), then constructs -`TruncatedGaussianPrior(mean, sigma, *limits)`. - -For a LogGaussian parameter with no config `Limits` entry: - -| | before #1527 | after #1527 | -|---|---|---| -| passed prior | `TruncatedGaussian(1.0, 0.5, -inf, inf)` | `TruncatedGaussian(1.0, 0.5, 0.0, inf)` | -| `value_for(0.001)` | **`-0.545`** | `0.0089` | -| `log_prior_from_value(-1.0)` | **`-8.0`** (finite) | `-inf` | - -The old behaviour was a strictly positive parameter being passed a prior that -samples negative values, so this is a correctness fix. But it **changes the -unit-cube mapping of the passed prior**, and a changed unit-cube mapping is a -changed inter-phase result. - -Note what is *not* at risk, so the check stays scoped: identifiers are -byte-identical (`__identifier_fields__ = ("mean", "sigma")` gates them), the -density is pointwise identical, and the direct (non-passed) unit-cube mapping is -identical over a 10-point grid. #1527 measured all three. Only **prior passing** -moved. - -## What to check - -1. **Does any PyAutoGalaxy / PyAutoLens model actually use `LogGaussianPrior`?** - Grep the config trees (`config/priors/**`) and the pipeline/search-chaining - code. If the answer is no, the task is done — record that and retire it. Do - not skip this step; it is most of the value here. -2. If yes, **does the priors config supply a `Limits` entry** for those - parameters? A config `Limits` entry wins over `prior.limits`, so a configured - parameter is unaffected. Only the unconfigured ones changed. -3. For any parameter that reaches the fallback, confirm the new lower-bounded - passed prior is what the science wants — it should be, since the parameter is - strictly positive, but confirm rather than assume. - -## Verify - -- An inter-phase prior-passing run (a search-chaining example touching a - LogGaussian parameter) completes and produces a passed prior whose - `lower_limit` is `0.0`, with no negative draws. -- State explicitly in the completion record which repos, configs and parameters - were checked, and whether any were affected — a null result here is the useful - answer and should be recorded as one, not left implicit. - -## Environment note - -Neither repo was attached when this prompt was filed; the grounding above is from -PyAutoFit `main` and from PyAutoFit#1527's measurements. Attach PyAutoGalaxy and -PyAutoLens before starting. From cba21f982c49a1239b56cc1af0ca1708e0c5d968 Mon Sep 17 00:00:00 2001 From: Claude Date: Thu, 27 Aug 2026 17:28:14 +0000 Subject: [PATCH 4/4] =?UTF-8?q?complete:=20PyAutoFit#1532=20merged=20?= =?UTF-8?q?=E2=80=94=20limits=20guard,=20VariableData.any,=20redundant=20o?= =?UTF-8?q?verrides?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit PyAutoFit#1532 merged as 6e2d8c8; issue #1531 closed. Two prompts retired, since both shipped in that one PR: - complete/2026/08/optimisation-state-limit-guard-truthiness.md - complete/2026/08/redundant-prior-limits-overrides.md The find worth keeping: VariableData.any reduced through var_all, so OptimisationState.valid accepted a parameter vector with only SOME components outside their limits, and MeanField's validity count under-reported the same way. Neither had any test coverage, so the suite stayed green through it — the #1477 process lesson, third time in this lineage. Also repoints the parent record's follow-up list, whose "No PR opened yet" the merge falsified. complete/index.md refreshed, dashboard regenerated, reconcile clean (0 suspects of 136), lifecycle.py check OK. Co-Authored-By: Claude --- .../loggaussian-prior-declares-own-support.md | 6 +- ...timisation-state-limit-guard-truthiness.md | 77 +++++++++++++++++++ .../08/redundant-prior-limits-overrides.md | 65 ++++++++++++++++ complete/index.md | 4 +- dashboard.html | 39 +++++----- dashboard.md | 58 ++++---------- 6 files changed, 181 insertions(+), 68 deletions(-) rename draft/refactor/autofit/optimisation_state_limit_guard_truthiness.md => complete/2026/08/optimisation-state-limit-guard-truthiness.md (52%) rename draft/refactor/autofit/redundant_prior_limits_overrides.md => complete/2026/08/redundant-prior-limits-overrides.md (54%) diff --git a/complete/2026/08/loggaussian-prior-declares-own-support.md b/complete/2026/08/loggaussian-prior-declares-own-support.md index 928e9d2d..96fc9ebd 100644 --- a/complete/2026/08/loggaussian-prior-declares-own-support.md +++ b/complete/2026/08/loggaussian-prior-declares-own-support.md @@ -129,8 +129,10 @@ part of #1527. All four were filed as prompts on 2026-08-27, alongside this record, and worked the same day: -- **1 and 2 implemented together** on PyAutoFit `claude/loggaussian-prior-support-ngh59x` - (`4c0f79b`, suite 2186/36 vs baseline 2178/36). No PR opened yet. +- **1 and 2 shipped together** as PyAutoFit#1532 (issue #1531), **MERGED** `6e2d8c8` on + 2026-08-27, CI green on all four legs, suite 2186/36 vs baseline 2178/36. Records: + `complete/2026/08/optimisation-state-limit-guard-truthiness.md` and + `complete/2026/08/redundant-prior-limits-overrides.md`. Unreleased (`pending-release`). - **Doing 1 exposed a live bug neither #1527 nor the prompt saw.** `VariableData.any` reduced through `var_all`, so it meant "is there a variable whose elements are ALL True" rather than "is ANY element True". `OptimisationState.valid` asks diff --git a/draft/refactor/autofit/optimisation_state_limit_guard_truthiness.md b/complete/2026/08/optimisation-state-limit-guard-truthiness.md similarity index 52% rename from draft/refactor/autofit/optimisation_state_limit_guard_truthiness.md rename to complete/2026/08/optimisation-state-limit-guard-truthiness.md index 3cc64190..1acd96dc 100644 --- a/draft/refactor/autofit/optimisation_state_limit_guard_truthiness.md +++ b/complete/2026/08/optimisation-state-limit-guard-truthiness.md @@ -1,3 +1,79 @@ +- issue: https://github.com/PyAutoLabs/PyAutoFit/issues/1531 (closed by the PR's `Closes` line) +- completed: 2026-08-27 +- pr: https://github.com/PyAutoLabs/PyAutoFit/pull/1532 (MERGED, merge `6e2d8c8`, head `4c0f79b`, + +167/-20 over 6 files, label `pending-release`) +- summary: `OptimisationState.valid`'s limits guards rewritten from truthiness to `is not None` — + and, underneath that tidy-up, a **live bug** fixed: `VariableData.any` reduced through `var_all`, + so the limits check accepted a parameter vector with *some* components out of bounds. +- validation: 2186 passed / 36 skipped (baseline 2178/36); CI green on all four legs. +- release: not performed; merged PR sits in the pending-release queue. +- sibling: shipped in the same PR as `redundant-prior-limits-overrides` — see that record. + +## The correction this task was really about + +The prompt was filed as `refactor` on the strength of a correction to **PyAutoFit#1527's own +follow-up list**, which described the guard as: + +> `line_search.OptimisationState.valid` uses `if self.lower_limit and …`, which is falsy at `0.0`. + +That is wrong, and believing it would have produced a bug report for a bug that does not exist. +`self.lower_limit` is not a scalar: it is a `VariableData` (`autofit/mapper/variable.py:309`, a +`Dict[Variable, np.ndarray]` subclass), built by `MeanField.lower_limit` and passed in by +`LaplaceOptimiser` only when `check_limits=True`. Dict truthiness is non-emptiness, so the guard was +`False` only for a model with **no free variables**, where the check is vacuous. There is no `0.0` +for it to be falsy at. And the EP path reads `m.lower_limit` off the **message**, which #1527 +deliberately left at `±inf` — so that code saw exactly what it saw before that fix. + +What remained was a readability and robustness defect: `if x` *reads* as a scalar test (which is how +it entered the follow-up list as a `0.0` bug) and was one type change away from being one — a scalar +would make `0.0` skip the check, a bare array would raise on the ambiguous truth value. + +## The bug underneath it — the actual find + +Writing a test for the rewritten guard is what exposed it. `VariableData.any`: + +```python +def any(self) -> bool: + return any(VariableData.var_all(self).values()) # var_all, not var_any +``` + +It reduced through **`var_all`**, so it meant *"is there a variable whose elements are ALL True"* +rather than *"is ANY element True"*. For `array([True, False])` it answered `False`. + +`OptimisationState.valid` asks `(self.parameters < self.lower_limit).any()`. So a parameter vector +with **some** components outside their limits was reported **valid**, and only a variable violating +on **every** component was caught. `MeanField`'s `if valid.any()` (`mean_field.py:501`) under-reported +the same way, inside an EP update loop. + +That, not the truthiness guard, is why the Laplace limits check under-enforced. Both fixes shipped +together, because the guard being tidied does not enforce anything without it. + +Blast radius, enumerated rather than assumed: the library has six `.any()` call sites. Four +(`graphical/utils.py:539`, `multi_start_gradient/search.py:468`, `analysis/latent.py:274`, plus the +numpy paths) are on numpy arrays and are untouched. The only `VariableData` consumers are +`line_search.py` (×2) and `mean_field.py:501`, and all three want a real `any`. + +## The process lesson, paying out a third time in this lineage + +`OptimisationState.valid` and `VariableData.any` had **no test coverage at all**. The full 2178-test +suite would have stayed green through any change to either — and did stay green through the bug, for +however long it had been there. + +This is the same lesson as `prior-support-clipper` (#1477), where a 1790-test suite passed against an +`LBFGS._fit` that raised `NameError` on every real call, and as `loggaussian-prior-declares-own-support` +(#1527), whose clipper test *asserted the bug as expected behaviour*. Three times in one lineage the +defect was in code nothing executed or nothing asserted about. + +The new `test_autofit/graphical/test_optimisation_state_valid.py` (8 tests) is verified by inversion: +reverting `var_any` to `var_all` fails 3 of them, including both partial-violation cases. + +## Repos / worktree + +- PyAutoFit: `claude/loggaussian-prior-support-ngh59x` (merged, deletable). +- No worktree — ran `web-github` against a direct clone. + +## Original prompt + # `OptimisationState.valid` guards on dict truthiness, not on "is a limit set" Type: refactor @@ -9,6 +85,7 @@ Autonomy: safe Priority: low Status: formalised Filed: 2026-08-27 +Issued: 2026-08-27 Follow-up owed by `complete/2026/08/loggaussian-prior-declares-own-support.md` (PyAutoFit#1526 / #1527), which listed it as an open item. diff --git a/draft/refactor/autofit/redundant_prior_limits_overrides.md b/complete/2026/08/redundant-prior-limits-overrides.md similarity index 54% rename from draft/refactor/autofit/redundant_prior_limits_overrides.md rename to complete/2026/08/redundant-prior-limits-overrides.md index 68b0b24c..d2751088 100644 --- a/draft/refactor/autofit/redundant_prior_limits_overrides.md +++ b/complete/2026/08/redundant-prior-limits-overrides.md @@ -1,3 +1,67 @@ +- issue: https://github.com/PyAutoLabs/PyAutoFit/issues/1531 (shared with its sibling; closed by the PR) +- completed: 2026-08-27 +- pr: https://github.com/PyAutoLabs/PyAutoFit/pull/1532 (MERGED, merge `6e2d8c8`, head `4c0f79b`, + label `pending-release`) +- summary: Deleted the three `limits` overrides PyAutoFit#1527 made exact duplicates of + `Prior.limits`, plus their now-unused `Tuple` imports — after measuring the one difference that + made it not a pure deletion. +- validation: 2186 passed / 36 skipped (baseline 2178/36); CI green on all four legs. +- release: not performed; merged PR sits in the pending-release queue. +- sibling: shipped in the same PR as `optimisation-state-limit-guard-truthiness` — the substantive + half of that PR, and the record worth reading. + +## What was deleted + +`UniformPrior` (`uniform.py:205`), `LogUniformPrior` (`log_uniform.py:199`) and +`TruncatedGaussianPrior` (`truncated_gaussian.py:122`) each carried + +```python +@property +def limits(self) -> Tuple[float, float]: + return self.lower_limit, self.upper_limit +``` + +which #1527 made redundant when it changed the base `Prior.limits` from a hardcoded +`(-inf, inf)` to `(float(self.lower_limit), float(self.upper_limit))`. + +## The measurement that made this safe — and why it was not obvious + +The base coerces with `float()`; the three overrides did not. So deleting them changes the *type* of +what `limits` returns, and that value is not inert — `AbstractPriorModel.mapper_from_prior_means` +feeds it straight into `TruncatedGaussianPrior(mean, sigma, *limits)` +(`prior_model/abstract.py:1153`). Two ways it could have bitten: + +1. **A numpy scalar becoming a Python float** in a constructed passed-prior. Measured: all three + priors already store Python `float`s (`UniformPrior.__init__` does `self.lower_limit = + float(lower_limit)` itself), so `float()` is a no-op. Values **and** types identical across all + five prior families, before and after. +2. **A JAX tracer hitting `float()` under `jit`** and raising `ConcretizationTypeError`. This looked + like the real risk, because `UniformPrior.tree_flatten` returns `(lower_limit, upper_limit, id)` + as pytree **children** — so under `jit` they genuinely are tracers. + +Point 2 turned out to be moot, for a reason worth recording: **a prior cannot cross a `jit` boundary +as an argument at all today.** With `autofit.jax.enable_pytrees()` on, `jax.jit(f)(prior)` raises +inside `tree_unflatten` → `UniformPrior.__init__`, at `self.lower_limit = float(lower_limit)`, before +any `limits` access happens. Verified for both the override path and the base path — identical +failure, with and without this change. So `limits` is unreachable under `jit` either way. + +That is a latent limitation in its own right (a class registered as a pytree node that cannot +actually survive the round-trip), and it is **not** fixed here. Noted, not filed. + +## Why this shipped in a two-prompt PR + +Against PyAutoMind's "one prompt = one task = one PR". Both prompts are the same cleanup left by +#1527, this half is three line deletions plus three import edits, and splitting would have put the +type measurement above in one PR and the deletions it licenses in another. Flagged in the PR body +and to the human before merge. + +## Repos / worktree + +- PyAutoFit: `claude/loggaussian-prior-support-ngh59x` (merged, deletable). +- No worktree — ran `web-github` against a direct clone. + +## Original prompt + # Three `limits` overrides are now exact duplicates of `Prior.limits` Type: refactor @@ -9,6 +73,7 @@ Autonomy: safe Priority: low Status: formalised Filed: 2026-08-27 +Issued: 2026-08-27 Follow-up owed by `complete/2026/08/loggaussian-prior-declares-own-support.md` (PyAutoFit#1526 / #1527), which named it and deliberately left it out to keep diff --git a/complete/index.md b/complete/index.md index 2515526e..d235c149 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. -1155 records across 7 buckets. +1157 records across 7 buckets. ## Highlights @@ -165,6 +165,7 @@ _(curate hard-won records here — survives regeneration.)_ - [numerical-hazard-profiling](2026/08/numerical-hazard-profiling.md) — Phase 1 established the reusable numerical-hazard profiling framework, typed risk measurements, reachability a… - [numerical-inversion-failures](2026/08/numerical-inversion-failures.md) - [one-tap-dashboard-rollout](2026/08/one-tap-dashboard-rollout.md) — the pattern (a generated page + one-tap 📋 copy-for-Claude payloads, phone +- [optimisation-state-limit-guard-truthiness](2026/08/optimisation-state-limit-guard-truthiness.md) — closed by the PR's `Closes` line - [optional-dependency-skip-guards](2026/08/optional-dependency-skip-guards.md) — `test_nautilus.py::test__single_core_builds_no_pool` had no skip guard - [organ-repo-spelling-splits](2026/08/organ-repo-spelling-splits.md) — closed on ship - [organism-board-final-readmes](2026/08/organism-board-final-readmes.md) — close on record @@ -212,6 +213,7 @@ _(curate hard-won records here — survives regeneration.)_ - [reconcile-upstream-repo-mode](2026/08/reconcile-upstream-repo-mode.md) - [reconstruction-noise-map-covariance-sqrt](2026/08/reconstruction-noise-map-covariance-sqrt.md) - [rectangular-spline-adapt-image-jit-fix](2026/08/rectangular-spline-adapt-image-jit-fix.md) +- [redundant-prior-limits-overrides](2026/08/redundant-prior-limits-overrides.md) — shared with its sibling; closed by the PR - [refactor-witness-map-audit](2026/08/refactor-witness-map-audit.md) — PyAutoBrain#269 → PR #271, merge cf969f3; stacked on #268 - [registry-integrity-check](2026/08/registry-integrity-check.md) - [regularization-jax-gradient-gaps](2026/08/regularization-jax-gradient-gaps.md) diff --git a/dashboard.html b/dashboard.html index 85f5f2c6..1de39053 100644 --- a/dashboard.html +++ b/dashboard.html @@ -204,7 +204,7 @@

PyAutoMindDashboard

Intent. Priority. Flow.

Every task the Mind is holding. Tap a task's 📋 and its /start_dev command is on your clipboard — paste it into a Claude Code chat to route Claude straight to that task. Recent is the same work by date — what has been happening rather than what to do next.

-
  • 1In flight
  • 3Parked
  • 5Planned
  • 138Backlog
+
  • 1In flight
  • 3Parked
  • 5Planned
  • 136Backlog

Last updated 2026-08-27. This page is generated from active/, draft/ and the registry files, so it is only as current as they are. dashboard_refresh.yml re-renders it on every push to main — that heals a stale page, but not a stale prompt: a task that shipped without its prompt advancing to complete/ keeps rendering here as pickable backlog. Reconciling those is the refresh below.

Split lensing regimes: multi_galaxy / group / cluster (epic plan)📖 docsautolenstoo-largesupervisedhigh

Re-baseline the MGE imaging JIT profiling regression value🧪 testautolens_workspace_developertoo-largesupervisedhigh

Quick wins (small enough, and safe enough to run unattended)

- - +

(none right now)

In flight markdown version

Issued — each has an open GitHub issue and usually a branch.

@PyAutoFit TransformedMessage.factor_gradient crashes on first callissue #1501 — issued 2026-08-19HOLD — do not start dev. Fix-or-delete hangs off the PyAutoFit#1498 logpdf-contract

@@ -270,7 +269,7 @@

Planned

latent-nan-guard-honest-run — planned 2026-07-22

Backlog markdown version

-

138 filed prompts, not started — sorted most-pickable first (priority, then size). 25 of them belong to an epic and are listed only under Epics below.

+

136 filed prompts, not started — sorted most-pickable first (priority, then size). 25 of them belong to an epic and are listed only under Epics below.

feature — 28 @@ -380,14 +379,12 @@

Backlog

plot coverage — follow-ups deferred from plot-coverage-gaps📖 docsworkspaces

-refactor — 8 +refactor — 6

Split Fitness.batch_size into lh_batch_size and latent_batch_size♻️ refactorautofitsmallsupervisednormal

One construction path for plane-bound lensing quantities♻️ refactorautolensmediumsupervisednormal

Remove the dead EDEN packaging tooling from PyAutoFit♻️ refactorpyautofitmediumsupervisednormal

- -

Deduplicate repos_sync.py's check/write pairs♻️ refactorpyautomindmediumsafelow

@@ -417,18 +414,6 @@

Backlog 2026-08-27 filed -OptimisationState.valid guards on dict truthiness, not on "is a… - - - -2026-08-27 -filed -Three limits overrides are now exact duplicates of Prior.limits - - - -2026-08-27 -filed Should TransformedMessage carry its own support, rather than the… @@ -474,13 +459,13 @@

Backlog wiki-currency's --check-version gate rots on every library main merge - + 2026-08-24 filed interferometer/jax_grad/gradient.py: eager and jitted likelihoods… - + 2026-08-24 filed autocti_workspace has no Navigator Check, so its CI can never roll… @@ -714,6 +699,18 @@

Backlog Nightly release has been blocked 8 nights running — triage the streak + +2026-08-04 +filed +HowToLens ch4 tutorial 3: mask overlay is never actually drawn + + + +2026-08-03 +filed +Un-park imaging/features/scaling_relation/slam — the PyAutoArray#431… + +

Epics markdown version

diff --git a/dashboard.md b/dashboard.md index 4c873711..0436a399 100644 --- a/dashboard.md +++ b/dashboard.md @@ -45,7 +45,7 @@ anything you could not verify. | [In flight](#in-flight) (`active/`) | 1 | | [Parked](#parked) (`parked.md`) | 3 | | [Planned](#planned) (`planned.md`) | 5 | -| [Backlog](#backlog) (`draft/`) | 138 | +| [Backlog](#backlog) (`draft/`) | 136 | ## Start here @@ -149,21 +149,7 @@ anything you could not verify. **Quick wins** (small enough, and safe enough to run unattended) -
📋 OptimisationState.valid guards on dict truthiness, not on "is a limit set" — autofit · small · safe · low - -``` -/start_dev draft/refactor/autofit/optimisation_state_limit_guard_truthiness.md -``` - -
- -
📋 Three limits overrides are now exact duplicates of Prior.limits — autofit · small · safe · low - -``` -/start_dev draft/refactor/autofit/redundant_prior_limits_overrides.md -``` - -
+- _(none right now)_ ## In flight @@ -261,7 +247,7 @@ Scoped but not started; some are not yet prompt files. Full detail in [`planned. ## Backlog -**138** filed prompts, not started. Each section is sorted most-pickable first (priority, then size). **25** of them belong to an epic and are listed only under [Epics](#epics) below. +**136** filed prompts, not started. Each section is sorted most-pickable first (priority, then size). **25** of them belong to an epic and are listed only under [Epics](#epics) below.
feature — 28 @@ -1033,7 +1019,7 @@ Scoped but not started; some are not yet prompt files. Full detail in [`planned.
-refactor — 8 +refactor — 6
📋 einstein_radius_jit_from: replace static init_guess with a JAX-native seed finder — autogalaxy · too-large · supervised · high @@ -1075,22 +1061,6 @@ Scoped but not started; some are not yet prompt files. Full detail in [`planned.
-
📋 OptimisationState.valid guards on dict truthiness, not on "is a limit set" — autofit · small · safe · low - -``` -/start_dev draft/refactor/autofit/optimisation_state_limit_guard_truthiness.md -``` - -
- -
📋 Three limits overrides are now exact duplicates of Prior.limits — autofit · small · safe · low - -``` -/start_dev draft/refactor/autofit/redundant_prior_limits_overrides.md -``` - -
-
📋 Deduplicate repos_sync.py's check/write pairs — pyautomind · medium · safe · low ``` @@ -1218,8 +1188,6 @@ The 50 newest things to happen to the work in hand, newest first — issued, par | Date | Event | Task | |------|-------|------| -| 2026-08-27 | filed | OptimisationState.valid guards on dict truthiness, not on "is a… | -| 2026-08-27 | filed | Three limits overrides are now exact duplicates of Prior.limits | | 2026-08-27 | filed | Should TransformedMessage carry its own support, rather than the… | | 2026-08-27 | filed | One construction path for plane-bound lensing quantities | | 2026-08-27 | filed | Multi-plane time delays | @@ -1228,13 +1196,13 @@ The 50 newest things to happen to the work in hand, newest first — issued, par | 2026-08-26 | filed | jax_profiling/gradient/imaging/pixelization.py: 3.2% of its pin move… | | 2026-08-26 | filed | The Brain board should work in a session that has no gh | | 2026-08-24 | filed | wiki-currency's --check-version gate rots on every library main merge | +| 2026-08-24 | filed | interferometer/jax_grad/gradient.py: eager and jitted likelihoods… | +| 2026-08-24 | filed | autocti_workspace has no Navigator Check, so its CI can never roll… |
… 10 more (40 left) | Date | Event | Task | |------|-------|------| -| 2026-08-24 | filed | interferometer/jax_grad/gradient.py: eager and jitted likelihoods… | -| 2026-08-24 | filed | autocti_workspace has no Navigator Check, so its CI can never roll… | | 2026-08-24 | filed | Un-park multi_galaxy/features/scaling_relation/slam once a capped run… | | 2026-08-24 | filed | Induct PyAutoReduce into the PyAutoHands release machinery (date… | | 2026-08-24 | filed | Heart's local smoke runner cannot run any CTI workspace — no autocti… | @@ -1243,13 +1211,13 @@ The 50 newest things to happen to the work in hand, newest first — issued, par | 2026-08-23 | filed | Brain board follow-ups: what real mornings surface | | 2026-08-22 | filed | smoke_install.sh's stale jax<0.7 pin — CI is on the right jax… | | 2026-08-22 | filed | Untrack the generated FITS test artifacts in autoarray | +| 2026-08-22 | filed | The reconstruction noise map describes a different estimator than the… | +| 2026-08-22 | filed | Point-source JSON datasets record no resolution regime |
… 10 more (30 left) | Date | Event | Task | |------|-------|------| -| 2026-08-22 | filed | The reconstruction noise map describes a different estimator than the… | -| 2026-08-22 | filed | Point-source JSON datasets record no resolution regime | | 2026-08-22 | filed | Is Intel macOS a supported platform, and what is the numpy-only… | | 2026-08-21 | filed | Rectangular mesh split: Bilinear (fast CPU default) vs RTU… | | 2026-08-20 | filed | Numba CPU likelihood phase 2: kernel-CDF numba fast path (the 49-88%… | @@ -1258,13 +1226,13 @@ The 50 newest things to happen to the work in hand, newest first — issued, par | 2026-08-19 | issued | @PyAutoFit TransformedMessage.factor_gradient crashes on first… | | 2026-08-19 | filed | Release board: local run_logs enrichment | | 2026-08-19 | filed | RTD organism docs currency: Nerves page, organ-count drift, hands.md… | +| 2026-08-19 | filed | Deduplicate repos_sync.py's check/write pairs | +| 2026-08-19 | filed | Bug in autocti_workspace: the dataset_1d results/database example… |
… 10 more (20 left) | Date | Event | Task | |------|-------|------| -| 2026-08-19 | filed | Deduplicate repos_sync.py's check/write pairs | -| 2026-08-19 | filed | Bug in autocti_workspace: the dataset_1d results/database example… | | 2026-08-18 | parked | single-source-density-design | | 2026-08-18 | parked | prior-message-collapse-design | | 2026-08-18 | filed | @PyAutoFit TransformedMessage.logpdf/pdf omit the transform… | @@ -1273,13 +1241,13 @@ The 50 newest things to happen to the work in hand, newest first — issued, par | 2026-08-14 | filed | Three jax_likelihood pins are stale by ~1.24e-4 and fail the smoke… | | 2026-08-09 | found | isothermal-ell-sph-oversampling-at-the-cusp | | 2026-08-08 | parked | pyautoreduce-slacs1430-acs-comparison | +| 2026-08-08 | filed | Regenerate autolens_workspace markdown/ so the MGE pages show… | +| 2026-08-07 | filed | Regenerate setup_notebook-drifted notebooks in… |
… 10 more (10 left) | Date | Event | Task | |------|-------|------| -| 2026-08-08 | filed | Regenerate autolens_workspace markdown/ so the MGE pages show… | -| 2026-08-07 | filed | Regenerate setup_notebook-drifted notebooks in… | | 2026-08-06 | filed | Triage: Convolver "No blurring_image provided" warning in canonical… | | 2026-08-06 | filed | Rewrite PyAutoCTI docs/api — 55 of 89 autosummary entries are dead | | 2026-08-06 | filed | Dependency-cap refresh 2026-08: safe bumps, astropy 8 decision, two… | @@ -1288,6 +1256,8 @@ The 50 newest things to happen to the work in hand, newest first — issued, par | 2026-08-04 | filed | cosmos_web_ring stores boolean masks as float64, wasting ~3.4 MB of… | | 2026-08-04 | filed | autolens_workspace_developer: broad stale-API rot (56 symbols, no CI) | | 2026-08-04 | filed | Nightly release has been blocked 8 nights running — triage the streak | +| 2026-08-04 | filed | HowToLens ch4 tutorial 3: mask overlay is never actually drawn | +| 2026-08-03 | filed | Un-park imaging/features/scaling_relation/slam — the PyAutoArray#431… |