diff --git a/batches/2026-08-31-am.md b/batches/2026-08-31-am.md
index 2fe5f29..c4c4e83 100644
--- a/batches/2026-08-31-am.md
+++ b/batches/2026-08-31-am.md
@@ -4,7 +4,12 @@
- shift: overnight 29th→31st, pre-framework
- lane: local-dev
- review-minutes-planned: 121 (packet estimate; no dispatch-time plan existed)
-- migrated-from: PyAutoMind/batches/2026-08-31-am.md
+- migrated-from: PyAutoMind/batches/2026-08-31-am.md (deleted from the Mind 2026-09-03 by
+ mind-post-cortex phase 2; this file and the packet/review beside it are now the record of
+ record — the Mind original survives only in that repo's git history)
+- usage-window-at-dispatch: n/a (pre-framework)
+- heart-ack: n/a (pre-framework — the runs were hand-dispatched before the batch doctrine existed)
+- expected-effects: n/a (the field did not exist at dispatch)
- members:
- failed_submissions_342008_10: phases/inference_programme/failed_submissions_342008_10.md — 342008, 342009, 342010 — 1 — ready
- smc_probe_342018: phases/inference_programme/smc_probe_342018.md — 342018 — 25 — ready
@@ -22,8 +27,22 @@
- review-minutes-actual: excluded — workshop session (the framework was built and polished mid-review); the first calibration point is the next batch
- notes: |
Transcribed into the Cortex grammar on 2026-09-01 (birth epic phase 4). The Mind
- original stays intact at PyAutoMind/batches/2026-08-31-am.md with a `- moved-to:`
- line — a verbatim history has to live somewhere live.
+ original carried a `- moved-to:` line and was kept live beside this one until
+ 2026-09-03, when mind-post-cortex phase 2 deleted the Mind trio (record, packet and
+ review) as science residue. The three facts that transcription had dropped were
+ ported here first and are recorded below; the packet HTML was already byte-identical
+ and the Mind review file was a strict subset of the Cortex one.
+
+ From the Mind original, ported 2026-09-03: the pre-framework `usage-window-at-dispatch`,
+ `heart-ack` and `expected-effects` `n/a` values are now header lines above. The Mind
+ record's eighth member `euclid-phase2-pr46` (a **dev** retrospective on the merged
+ PR #46, judged `structure-ok`, 35 review-minutes) is named here and stays untranscribed
+ as a science member, for the reason given below. The Mind's own tweaks-queued note
+ listed the three follow-ups that became its queue entries — the subhalo follow-up wave,
+ the euclid docs/structure tidy and the numba-vs-JAX-sparse intake — all of which the
+ review file below already records. The packet defect fixed same-day was Mind commit
+ 6491b09c (the `TEMPLATE.md` standalone-document rule); the rewind it references is
+ Mind commit 2fb40b45 / autolens_profiling#200.
Two shape differences from the Mind record, both forced by the Cortex grammar:
the Mind's single member `subhalo-pl_eff-342027_12` covered two lenses on one
diff --git a/dashboard.html b/dashboard.html
index 2bedfe9..c73d3cd 100644
--- a/dashboard.html
+++ b/dashboard.html
@@ -265,8 +265,8 @@
Long-running programmes whose science half lives here; each card links its development half in the Mind.
prepare the Euclid DR1 modelling pipeline, then prove it on real DR1 lenses — Mind half: PyAutoMind/epics.md#euclid-dr1-prep — ledger: phases/euclid/ — science half opens at phase 4, gated on the 3a PR and the 3b issue; 5–7 planned behind it
-
profile and certify the JAX inference stack — samplers, meshes, reference baselines — Mind half: PyAutoMind/epics.md#jax-inference-profiling — ledger: autolens_profiling/results/notes/inference/PROGRAMME.md — Phase 1 RULED 2026-09-02 (R-20260902-01, accept scoped to the four positions-on rows) and closed out; certified InferenceRefs_v1 set = 6 baselines (mge_fp64, mge_pos_fp64, delaunay_nn_pos, slam_source_pix_pos, slam_source_pix_nn_pos, delaunay_pos); Step 2 filed 2026-09-02 as phase 12 (RAL 342241, tasks 11–13, positions-on refs for pixelization / knn / delaunay_matern; submitted); Step 3 ruled 2026-09-02 — phases 13/14/15 accepted (R-20260902-08/09/10), Gates A, B pt 1, B pt 2 re-called on the legacy MGE evidence (Gate A's Delaunay leg struck); next: rule phase 12 when pulled, then Phase 5 positions-on (penalty factor to be decided — 1e5 recommended per Gate B pt 2)
-
expectation propagation as the scalable alternative to graphical joint fits — Mind half: PyAutoMind/epics.md#graphical-ep — ledger: PyAutoMind/research/graphical_ep/ep_campaign.md — both science phases planned; the projects are dormant and the campaign's
+
profile and certify the JAX inference stack — samplers, meshes, reference baselines — Mind half: none — the Mind entry was retired 2026-09-03 (mind-post-cortex phase 2); the — ledger: autolens_profiling/results/notes/inference/PROGRAMME.md — Phase 1 RULED 2026-09-02 (R-20260902-01, accept scoped to the four positions-on rows) and closed out; certified InferenceRefs_v1 set = 6 baselines (mge_fp64, mge_pos_fp64, delaunay_nn_pos, slam_source_pix_pos, slam_source_pix_nn_pos, delaunay_pos); Step 2 filed 2026-09-02 as phase 12 (RAL 342241, tasks 11–13, positions-on refs for pixelization / knn / delaunay_matern; submitted); Step 3 ruled 2026-09-02 — phases 13/14/15 accepted (R-20260902-08/09/10), Gates A, B pt 1, B pt 2 re-called on the legacy MGE evidence (Gate A's Delaunay leg struck); next: rule phase 12 when pulled, then Phase 5 positions-on (penalty factor to be decided — 1e5 recommended per Gate B pt 2)
+
expectation propagation as the scalable alternative to graphical joint fits — Mind half: PyAutoMind/epics.md#graphical-ep — ledger: PyAutoMind/draft/research/graphical_ep/ep_campaign.md — both science phases planned; the projects are dormant and the campaign's
the Source & Cluster arc — cluster mass modelling and pixelized source inference — Mind half: PyAutoMind/epics.md#cluster-strong-lensing — ledger: PyAutoMind/draft/feature/autolens/source_cluster_arc.md — one science phase (arc phase 11), planned behind the arc's phase 10
The science body map — where each project lives, what syncs it and where its witness lands.
diff --git a/dashboard.md b/dashboard.md
index 1e640bf..ce886f7 100644
--- a/dashboard.md
+++ b/dashboard.md
@@ -137,7 +137,7 @@ Work out where the Cortex half of the epic euclid-dr1-prep stands from phases/eu
-📋 profile and certify the JAX inference stack — samplers, meshes, reference baselines — Mind half: PyAutoMind/epics.md#jax-inference-profiling — ledger: autolens_profiling/results/notes/inference/PROGRAMME.md — Phase 1 RULED 2026-09-02 (R-20260902-01, accept scoped to the four positions-on rows) and closed out; certified InferenceRefs_v1 set = 6 baselines (mge_fp64, mge_pos_fp64, delaunay_nn_pos, slam_source_pix_pos, slam_source_pix_nn_pos, delaunay_pos); Step 2 filed 2026-09-02 as phase 12 (RAL 342241, tasks 11–13, positions-on refs for pixelization / knn / delaunay_matern; submitted); Step 3 ruled 2026-09-02 — phases 13/14/15 accepted (R-20260902-08/09/10), Gates A, B pt 1, B pt 2 re-called on the legacy MGE evidence (Gate A's Delaunay leg struck); next: rule phase 12 when pulled, then Phase 5 positions-on (penalty factor to be decided — 1e5 recommended per Gate B pt 2)
+📋 profile and certify the JAX inference stack — samplers, meshes, reference baselines — Mind half: none — the Mind entry was retired 2026-09-03 (mind-post-cortex phase 2); the — ledger: autolens_profiling/results/notes/inference/PROGRAMME.md — Phase 1 RULED 2026-09-02 (R-20260902-01, accept scoped to the four positions-on rows) and closed out; certified InferenceRefs_v1 set = 6 baselines (mge_fp64, mge_pos_fp64, delaunay_nn_pos, slam_source_pix_pos, slam_source_pix_nn_pos, delaunay_pos); Step 2 filed 2026-09-02 as phase 12 (RAL 342241, tasks 11–13, positions-on refs for pixelization / knn / delaunay_matern; submitted); Step 3 ruled 2026-09-02 — phases 13/14/15 accepted (R-20260902-08/09/10), Gates A, B pt 1, B pt 2 re-called on the legacy MGE evidence (Gate A's Delaunay leg struck); next: rule phase 12 when pulled, then Phase 5 positions-on (penalty factor to be decided — 1e5 recommended per Gate B pt 2)
```
Work out where the Cortex half of the epic jax-inference-profiling stands from autolens_profiling/results/notes/inference/PROGRAMME.md and tell me what its next phase is.
@@ -145,10 +145,10 @@ Work out where the Cortex half of the epic jax-inference-profiling stands from a
-📋 expectation propagation as the scalable alternative to graphical joint fits — Mind half: PyAutoMind/epics.md#graphical-ep — ledger: PyAutoMind/research/graphical_ep/ep_campaign.md — both science phases planned; the projects are dormant and the campaign's
+📋 expectation propagation as the scalable alternative to graphical joint fits — Mind half: PyAutoMind/epics.md#graphical-ep — ledger: PyAutoMind/draft/research/graphical_ep/ep_campaign.md — both science phases planned; the projects are dormant and the campaign's
```
-Work out where the Cortex half of the epic graphical-ep stands from PyAutoMind/research/graphical_ep/ep_campaign.md and tell me what its next phase is.
+Work out where the Cortex half of the epic graphical-ep stands from PyAutoMind/draft/research/graphical_ep/ep_campaign.md and tell me what its next phase is.
```
diff --git a/epics.md b/epics.md
index f67b408..d467b78 100644
--- a/epics.md
+++ b/epics.md
@@ -18,8 +18,12 @@ Schema per entry — the Mind's, plus one field:
- notes:
```
-`- mind-half:` names the Mind entry by slug; the Mind's entries carry the
-reciprocal `- cortex-half:`. The four split epics below moved their science
+`- mind-half:` names the Mind entry by slug, or `none` when the epic moved
+whole. The Mind carried a reciprocal `- cortex-half:` key until 2026-09-03,
+when mind-post-cortex phase 2 removed it — nothing read it, and the pointer
+now lives in the Mind entry's own `notes:` as a "Science half:" clause. This
+file's `- mind-half:` stays the machine-readable half of the join.
+The four split epics below moved their science
halves here in phase 4 of the birth epic (2026-09-01): `euclid-dr1-prep`
phases 4, 5, 6a and 6b (renumbered here as Cortex phases 4–7);
`jax-inference-profiling` whole; the science halves of `graphical-ep` phases
@@ -45,7 +49,8 @@ This file is ledger: it auto-merges (`scripts/ledger_merge.py`).
## jax-inference-profiling
- title: profile and certify the JAX inference stack — samplers, meshes, reference baselines
- ledger: autolens_profiling/results/notes/inference/PROGRAMME.md
-- mind-half: PyAutoMind/epics.md#jax-inference-profiling
+- mind-half: none — the Mind entry was retired 2026-09-03 (mind-post-cortex phase 2); the
+ whole programme lives here, and its slices ship as autolens_profiling issues/PRs
- status: Phase 1 RULED 2026-09-02 (R-20260902-01, accept scoped to the four positions-on rows) and closed out; certified InferenceRefs_v1 set = 6 baselines (mge_fp64, mge_pos_fp64, delaunay_nn_pos, slam_source_pix_pos, slam_source_pix_nn_pos, delaunay_pos); Step 2 filed 2026-09-02 as phase 12 (RAL 342241, tasks 11–13, positions-on refs for pixelization / knn / delaunay_matern; submitted); Step 3 ruled 2026-09-02 — phases 13/14/15 accepted (R-20260902-08/09/10), Gates A, B pt 1, B pt 2 re-called on the legacy MGE evidence (Gate A's Delaunay leg struck); next: rule phase 12 when pulled, then Phase 5 positions-on (penalty factor to be decided — 1e5 recommended per Gate B pt 2)
- notes: The whole epic's science half lives here. The 2026-08-31 REWIND quarantined
every mesh / pixelization result to `output/legacy_wrong/` and every MGE result to
@@ -61,7 +66,7 @@ This file is ledger: it auto-merges (`scripts/ledger_merge.py`).
## graphical-ep
- title: expectation propagation as the scalable alternative to graphical joint fits
-- ledger: PyAutoMind/research/graphical_ep/ep_campaign.md
+- ledger: PyAutoMind/draft/research/graphical_ep/ep_campaign.md
- mind-half: PyAutoMind/epics.md#graphical-ep
- status: both science phases planned; the projects are dormant and the campaign's
development phases run first
diff --git a/phases/inference_programme/cluster_gradient_search_benchmark.md b/phases/inference_programme/cluster_gradient_search_benchmark.md
new file mode 100644
index 0000000..b8f427c
--- /dev/null
+++ b/phases/inference_programme/cluster_gradient_search_benchmark.md
@@ -0,0 +1,68 @@
+# Inference_programme — phase 17: cluster-scale gradient-search benchmark (Prodigy vs Nautilus, point-source)
+
+Project: inference_programme
+Phase: 17
+State: planned
+Gates:
+Witness: a cluster point-source sweep whose `results/searches/…` JSONs carry, per arm, best logL against the truth-instance logL, per-parameter recovery, wall time and evals/steps — the same truth-anchored shape as the 2026-07-31 galaxy-scale rows
+Budget: 24:00
+Runs:
+Ruling:
+Lane: local-dev
+Review-minutes: 20
+Filed: 2026-07-31
+Migrated-from: PyAutoMind/draft/research/autolens_profiling/cluster_gradient_search_benchmark.md
+
+## Question
+
+Does the Nautilus-vs-MultiStartProdigy comparison hold at CLUSTER scale for point
+sources — the regime the solved variants target (many sources, big dimensionality
+win: −2 params/source with `PointSolved`)?
+
+> **ABSORBED 2026-07-31 (same day it was filed)** into the Mind prompt
+> `draft/feature/autolens/point_source_defaults_campaign.md` (phase B, cluster
+> tier, on RAL A100s). Do not dispatch this phase standalone — it is carried here
+> so the context literals below survive the Cortex split and so the campaign's
+> cluster tier has a phase to rule against when it runs.
+
+Human-requested follow-up (2026-07-31, #657 wrap-up): once the galaxy-scale
+point_source benchmark cells wrap, extend the comparison to a cluster-scale
+point-source model in autolens_profiling.
+
+### Scope (autolens_profiling)
+
+- New sweep cells: `nautilus` + `multi_start_prodigy` on a cluster point-source dataset
+ (multi-plane tracer, multiple sources; reuse/extend the profiling simulators — the
+ workspace `cluster/simulator.py` family CSV conventions are the model source).
+- Model: dPIE/Isothermal members + host halo as in the workspace cluster examples;
+ sources as `al.ps.PointSolved` + `FitPositionsSourceSolved` (recommended search config)
+ with an image-plane-solved arm if runtime permits.
+- KNOWN CONSTRAINT: free cosmology cannot cross the solver custom_jvp boundary
+ (Tracer aux; see the Mind's `ideas.md` follow-up) — pin cosmology, or benchmark
+ source-plane solved only (no solver in chain) until the flattening follow-up lands.
+- Compare: best logL vs truth-instance logL, per-parameter recovery, wall time,
+ evals/steps; same truth-anchored methodology as the 2026-07-31 galaxy-scale runs.
+
+### Context literals (2026-07-31 galaxy-scale runs, results/searches/)
+
+- image_plane truth logL +7.20: nautilus +9.56 (739.7s) converged; prodigy 64x300 −79.9
+ (852.8s) missed the 5mas basin (PairAll −inf underflow plateaus suspected; 256-start
+ rerun pending at filing time).
+- source_plane truth logL −33788: BOTH found better-than-truth wrong models (nautilus
+ −313, prodigy −110 at 8.7x less wall) — scalar-mu^2 free-centre source-plane bias
+ displayed; gradients work, likelihood flavour is the problem.
+
+## Witness
+
+Per arm, a `results/searches/…` JSON carrying best logL against the truth-instance
+logL, per-parameter recovery, wall time and evals/steps — the same shape as the
+galaxy-scale rows above, so the two tiers are directly comparable.
+
+## Where to look
+
+- `/home/jammy/Code/PyAutoLabs/autolens_profiling/results/searches/`
+- `/mnt/ral/jnightin/autolens_profiling/`
+
+## Runs
+
+## Ruling
diff --git a/phases/inference_programme/legacy_point_output_sweep.md b/phases/inference_programme/legacy_point_output_sweep.md
new file mode 100644
index 0000000..1f382ed
--- /dev/null
+++ b/phases/inference_programme/legacy_point_output_sweep.md
@@ -0,0 +1,89 @@
+# Inference_programme — phase 19: sweep the RAL active output/ tree into output/legacy_point/
+
+Project: inference_programme
+Phase: 19
+State: planned
+Gates: autolens_profiling#205
+Witness: `output/legacy_point/` exists on RAL and on the laptop mirror holding all four families with their directory structure preserved; the active `output/` tree no longer contains them; a spot check finds any moved run under its new path; nothing deleted; a dated one-line note appended beside the ledger
+Budget: 2:00
+Runs:
+Ruling:
+Lane: local-dev
+Review-minutes: 5
+Filed: 2026-09-01
+Migrated-from: PyAutoMind/draft/maintenance/autolens_profiling/legacy_point_output_sweep.md
+
+## Question
+
+Can the RAL active `output/` tree be reduced to only what the redo can cite, by
+moving (never deleting) the spent families into `output/legacy_point/`?
+
+Note: autolens_profiling#205 was opened 2026-09-01 as this work's Cortex gate ref —
+reuse it, never open a second.
+
+### Where this came from
+
+The 2026-08-31-pm batch review's `delaunay-fp64-retro-baseline` rejection
+(2026-09-01) carried a programme-wide finding *and* an output-tree directive.
+The human inspected
+`output/legacy_wrong/searches/nautilus/imaging/delaunay/hst/hpc_a100_fp64/` and
+identified the solution as a demagnified-source unphysical solution — the
+classic Inversion bias `PositionsLH` exists to remove. The binding consequence
+for the redo is that every mesh / pixelization run must carry a `PositionsLH`
+positions penalty; mesh rows produced without it are not citable as references.
+
+The directive this phase enacts is the second half of that ruling, recorded
+verbatim in `autolens_profiling/results/notes/inference/DECISIONS.md` (entry
+"2026-09-01 — Batch 2026-08-31-pm review rulings", lines 1553-1559):
+
+> **Output-tree consequence (human directive, verbatim intent):** clear the RAL
+> active `output/` of all mesh results, the `image_plane`/`source_plane` point
+> source fits, the `Cluster` folder and the `point_source` folder — moved (not
+> deleted) to a new `output/legacy_point/` folder, so the active tree is clean and
+> updates with each task. Laptop action, queued as a local-dev prompt in the Mind.
+
+### What to do
+
+**Move, do not delete.** Everything named below is relocated into a new
+`output/legacy_point/` folder that mirrors the source tree's shape, so any of it
+can be cited or pulled back:
+
+1. all mesh results in the RAL active `output/` tree;
+2. the `image_plane` and `source_plane` point-source fits;
+3. the `Cluster` folder;
+4. the `point_source` folder.
+
+Do the same on the laptop mirror (`/mnt/c/Users/Jammy/Science/inference_programme/`)
+so the two trees stay in step — a sweep on one side only reintroduces the drift
+this is meant to remove.
+
+**This is a laptop action.** It needs the SSH endpoint to RAL and the local
+mirror, so it cannot run in a cloud session. Nothing is submitted, cancelled or
+modified on the cluster — this is a file move, which is why `Runs:` stays empty.
+
+### Why it matters
+
+The active `output/` tree is what each new task reads and updates. Leaving the
+quarantined and superseded families in it means every subsequent read has to
+know which subtrees are spent, which is exactly the state the 2026-08-31 rewind
+was meant to end. After the sweep the active tree holds only work the redo can
+actually cite.
+
+## Witness
+
+- `output/legacy_point/` exists on RAL and on the laptop mirror and holds all
+ four families named above, with their directory structure preserved.
+- The active `output/` tree no longer contains them.
+- Nothing was deleted — a spot check can find any moved run under its new path.
+- A one-line note appended beside the ledger recording that the sweep ran and on
+ what date.
+
+## Where to look
+
+- `/mnt/ral/jnightin/autolens_profiling/output/`
+- `/mnt/c/Users/Jammy/Science/inference_programme/`
+- `autolens_profiling/results/notes/inference/DECISIONS.md` (2026-09-01 entry)
+
+## Runs
+
+## Ruling
diff --git a/phases/inference_programme/multiband_compile_census_completion.md b/phases/inference_programme/multiband_compile_census_completion.md
new file mode 100644
index 0000000..a9c6297
--- /dev/null
+++ b/phases/inference_programme/multiband_compile_census_completion.md
@@ -0,0 +1,58 @@
+# Inference_programme — phase 18: multi-band compile census completion — A100/multi-core + hetero GPU rows
+
+Project: inference_programme
+Phase: 18
+State: planned
+Gates:
+Witness: the A100/multi-core and `datacube_img_hetero` GPU rows present in the multi-band census results, each with its backend verified from the results path rather than the SLURM partition, plus a written verdict on the two remaining secondary levers in `scripts/misc/jax_compile/README.md`
+Budget: 12:00
+Runs:
+Ruling:
+Lane: local-dev
+Review-minutes: 20
+Filed: 2026-07-30
+Migrated-from: PyAutoMind/draft/research/autolens_profiling/multiband_compile_census_completion.md
+
+## Question
+
+Do the confirmatory census legs of the multi-band compile matrix — the rows that
+were out of scope when the dominant driver was fixed at the source — change the
+picture?
+
+Follow-up to the multiband-pyloop-batching ship (PyAutoFit#1430 → PR#1431 +
+autolens_profiling#95, merged 2026-07-30). The dominant driver is fixed at the
+source (Python-loop batching + jitted broad-start filter in
+`AbstractMultiStartGradient`); these are the confirmatory legs left over:
+
+1. **A100 / multi-core rows** for the multi-band matrix (`datacube_img` /
+ `datacube_img_hetero` × `pyloop_vag` / `laxmap_vag` at production widths) —
+ re-run `sbatch /mnt/ral/jnightin/pixgrad_logs/census_gpu.sbatch` (or the
+ local pattern in `scripts/misc/jax_compile/probe.py`) once the RAL A100s
+ free up. Verify backend from the results path, never the partition (the
+ silent-CPU-fallback trap from #93).
+2. **`datacube_img_hetero` GPU rows** on the laptop RTX 2060 — quantify the
+ heterogeneity multiplier under the CUDA pipeline (the laptop GPU rows so far
+ are homogeneous only; the scan explosion proved CPU-backend-specific, tags
+ `mb_homo_cold_{pyloop,laxmap}_gpu`).
+3. **Verdict on the remaining secondary levers** — with the transform fixed,
+ reassess whether the band-padding/shape-canonicalization helper and the
+ per-factor jit boundary (heterogeneity-multiplier attacks, README verdict
+ bullets) are still worth building, and either file them or close them in
+ `scripts/misc/jax_compile/README.md`.
+
+## Witness
+
+The A100/multi-core rows and the `datacube_img_hetero` GPU rows present in the
+census results with the backend read off the results path (not the partition),
+and the secondary-lever verdict written into
+`scripts/misc/jax_compile/README.md` — filed as follow-ups or closed, not left open.
+
+## Where to look
+
+- `autolens_profiling/results/notes/multiband_pyloop_productized.md`
+- `autolens_profiling/scripts/misc/jax_compile/README.md` (multi-band section)
+- `/mnt/ral/jnightin/pixgrad_logs/census_gpu.sbatch`
+
+## Runs
+
+## Ruling
diff --git a/phases/slope_hierarchy/methods_writeup.md b/phases/slope_hierarchy/methods_writeup.md
new file mode 100644
index 0000000..7036e05
--- /dev/null
+++ b/phases/slope_hierarchy/methods_writeup.md
@@ -0,0 +1,97 @@
+# Slope_hierarchy — phase 2: methods write-up (NUTS headline, EP cautionary)
+
+Project: slope_hierarchy
+Phase: 2
+State: planned
+Gates:
+Witness: prose plus figures and tables committed under `paper/` in the slope_hierarchy checkout, every number regenerated from the committed `results/` JSONs rather than hand-copied, and the scope decision (methods note vs publishable short paper) recorded in the file itself
+Budget: 8:00
+Runs:
+Ruling:
+Lane: local-dev
+Review-minutes: 20
+Epic: graphical-ep
+Filed: 2026-07-22
+Migrated-from: PyAutoMind/draft/research/graphical_ep/slope_hierarchy_methods_writeup.md
+
+## Question
+
+What does the `slope_hierarchy` project's finished N=5 result actually claim, written down?
+
+`slope_hierarchy` (private, Jammy2211/slope_hierarchy#1, external checkout
+`/mnt/c/Users/Jammy/Science/slope_hierarchy`) answered all four of its goals and
+was wrapped up on 2026-07-22 with every result committed. `paper/figures/` and
+`paper/tables/` exist but are **empty** — nothing has been written yet.
+
+This phase produces no runs of its own: the runs are done and committed. It is a
+write-up phase, and its deliverable is prose, so `Runs:` stays empty and the
+ruling is a judgement on the written claim.
+
+### What there is to write about
+
+The project has an unusually clean methods story, because the negative result is
+sharp and fully diagnosed rather than hand-waved:
+
+1. **Goal 1 — JAX-gradient NUTS works.** BlackJAX NUTS on the joint factor graph
+ recovers the parent mean 2.028 [2.000, 2.063] and scatter 0.143 [0.117, 0.185]
+ against truth (2.0, 0.1). This is the headline method.
+2. **Goal 2 — EP recovers the mean but not the scatter.** Converged EP gives mean
+ 2.051 (agrees with NUTS) but scatter 0.026 — ~4× low — with errors ~1000× too
+ tight. Crucially this was separated from two innocent explanations before
+ being called a failure mode: the **projection bug** (real, found here, fixed
+ in PyAutoFit#1383) and **under-convergence** (the round-1 `max_steps=5` value
+ of 0.004 was a badly under-converged snapshot; the true fixed point is 0.026,
+ reached at `max_steps=12` and flat for the last ~2 sweeps).
+3. **Goal 3 — the RAL HPC pipeline** works end to end.
+4. **Goal 4 — the 2026-07 EP diagnostics wave** (PyAutoFit#1330/#1335) was
+ exercised in anger and **caught a real projection bug**, shipped as
+ PyAutoFit#1383. That is the strongest possible endorsement of the diagnostics:
+ they found a defect in the framework that ran them.
+5. **The generalisation.** A follow-up CPU toy (PyAutoFit#1405) proved the
+ scatter pathology is a **framework property, not a property of this problem** —
+ it reproduces off-boundary (parent σ=10) and off-lensing, as *stochastic
+ instability* across identical runs: 70% recover / 7% collapse / 23% crash.
+ The honest methods claim is therefore about EP-for-scale-hyperparameters in
+ general, not about N=5 strong lenses.
+
+### The claim to land
+
+**Use a joint gradient sampler (NUTS) for a hierarchical scale hyperparameter;
+EP is fast and correct for the parent mean but over-confident and
+variance-shrinking on the scatter.** Say plainly that the failure is stochastic
+(a basin, entered ~7% of the time on a clean toy) rather than a fixed bias — a
+reader who runs EP once and recovers the truth has not disproved this.
+
+### Scope decision to make first
+
+Two honest shapes, and this should be settled before drafting:
+
+- a **methods note / project write-up** in `paper/` for internal use and to
+ ground the PyAutoFit issues; or
+- a **publishable short paper**, which realistically wants the N=25–50 scale-up
+ (phase 1, `phases/slope_hierarchy/n25_scale_up.md`) first, since N=5 is thin
+ for a headline parity claim.
+
+Prefer the note unless the scale-up has already run — the note costs a session
+and preserves everything; the paper does not.
+
+## Witness
+
+Prose plus figures and tables committed under `paper/`, with the figures
+regenerated from the committed `results/` JSONs (do not hand-copy numbers — they
+are all in `results/*.json` and
+`results/ep_history_n5_maxsteps12/parent_trajectory.csv`). The scope decision
+(note vs paper) is recorded in the written file, not left implicit.
+
+The repo is private and personal — keep the prose and figures out of public
+repos.
+
+## Where to look
+
+- `/mnt/c/Users/Jammy/Science/slope_hierarchy/results/`
+- `/mnt/c/Users/Jammy/Science/slope_hierarchy/results/ep_history_n5_maxsteps12/parent_trajectory.csv`
+- `/mnt/c/Users/Jammy/Science/slope_hierarchy/paper/`
+
+## Runs
+
+## Ruling