diff --git a/draft/bug/pyautobuild/release_version_sync_back_to_main.md b/complete/2026/08/release-version-sync-back-to-main.md
similarity index 72%
rename from draft/bug/pyautobuild/release_version_sync_back_to_main.md
rename to complete/2026/08/release-version-sync-back-to-main.md
index 54b32c50..4e5264a8 100644
--- a/draft/bug/pyautobuild/release_version_sync_back_to_main.md
+++ b/complete/2026/08/release-version-sync-back-to-main.md
@@ -1,3 +1,83 @@
+# release-version-sync-back-to-main
+
+**Completed:** 2026-08-18 (underlying work merged by 2026-07-22)
+**Type:** bug · **Target:** PyAutoBuild/PyAutoHands (version model) · **No new
+code PR** — this record retires the Phase 4 tracker prompt after verifying every
+sub-task against the code on `main` (branch
+`claude/release-version-stamps-pins-sync-7arym7`).
+
+## Summary
+
+The "release does not sync `__version__` stamps and workspace pins back to main"
+prompt was the **Phase 4 tracker** of the build-chain integrity campaign
+(PyAutoHands#155, closed 2026-07-22 — see
+[[build-chain-umbrella]]). The human decision of 2026-07-16 (re-confirmed
+2026-07-19) resolved its central fork as **(b): mains stay authoritative** — no
+stamp/pin commit-backs to `main`; instead fix the consumers that misread a
+compatibility floor as an exact pin, and enforce "a floor must name an
+installable release". All four Phase 4 tasks then shipped, but the final step
+the DECIDED section prescribed — *"rewrite or shelve this prompt with the
+reasoning recorded"* — never ran, so the prompt sat in `draft/` for a month
+showing on the dashboard as a not-started high-priority bug. This record is that
+missing step.
+
+## Verified 2026-08-18 against the code on `main` (not the tracker)
+
+Per the campaign's own lesson ("verify state at the source before working from
+any tracker"), each claim was re-checked in fresh clones of the repos' `main`:
+
+- **Task 1 — floors adopted** ([[floors-adoption]], 7 workspace PRs merged):
+ `config/general.yaml` carries `version.minimum_library_version: 2026.7.9.1`
+ in autofit_workspace and HowToFit (spot-checked); the value names a
+ non-yanked, installable release.
+- **Task 2 — Heart `version_skew` floor rework** (PyAutoHeart#96):
+ `heart/checks/version_skew.py` compares each workspace floor against the
+ newest `YYYY.M.D.B` release tag — UNSATISFIABLE (floor > newest release) /
+ OK / BAD / UNKNOWN. The previously unfailable "floor names an installable
+ release" invariant is now enforced.
+- **Task 3 — assistant `--check-version` de-gated** (autolens_assistant#80 +
+ clone mirrors): `autoassistant/audit_skill_apis.py::check_version` gates on
+ the public-API-surface hash only; its docstring cites "#155 Phase 4 task 3"
+ and explains the structurally-permanent source-vs-wheel `__version__` false
+ positive that the equality gate produced.
+- **Task 4 — orphaned README pins removed** (autofit_workspace#107,
+ autofit_workspace_test#64, autolens_workspace_test#198, PyAutoHands#174):
+ autofit_workspace's README carries no version pin; PyAutoHands
+ `pre_build.sh` has the `VERSION` variable and README-pin sed deleted, with an
+ in-file comment recording the decision and warning "do not re-add a README
+ version bump here or on the runner".
+- **Legacy key removal, incl. the HowToFit holdout** (HowToFit#23 and the
+ version-model-honesty epic): no `workspace_version:` key and no
+ `version.txt` in the checked workspaces; only `workspace_version_check`
+ (the bypass toggle) remains.
+
+## Key findings / traps
+
+- **The symptom was real; the proposed fix was rejected twice.** Fork (a)
+ (re-add stamp/pin commit-backs on release) was what the prompt title asked
+ for, and it was deliberately rejected — PyAutoBuild#120/#121 removed those
+ commit-backs after they caused stale CI storms, an email flood and an
+ org-wide cron pause. `__version__` on a library `main` is frozen **by
+ design**; the floor (`version.minimum_library_version`), the release tags
+ and the wheels are the live version signals.
+- **The recurring "drift" alarms were consumer bugs**, not missing commits:
+ three consumers read a floor as an exact pin. Deleting the false signal (the
+ assistant's `__version__` equality gate) worked where documenting the trap
+ twice had not.
+- **The genuine bug either way** — floors naming the yanked `2026.7.6.649` —
+ is closed: floors now name installable releases and `version_skew` RED-flags
+ a floor newer than any release. The residual gap (a floor naming a release
+ later *yanked on PyPI* — needs the PyPI API, not git tags) is acknowledged at
+ `version_skew.py:33` and now filed as
+ `draft/feature/pyautoheart/version_skew_yank_awareness.md` so it survives
+ this tracker's retirement.
+- **Tracker-drift lesson, again:** the dashboard listed this as a pickable
+ not-started bug for a month after the work merged. A tracker prompt whose
+ execution moved into an umbrella campaign must be retired by the campaign's
+ closeout, or it becomes exactly the stale index the campaign warned about.
+
+## Original prompt
+
# Release does not sync __version__ stamps and workspace pins back to main
Type: bug
diff --git a/complete/index.md b/complete/index.md
index ec1a7249..a28745c8 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.
-1017 records across 7 buckets.
+1018 records across 7 buckets.
## Highlights
@@ -120,6 +120,7 @@ _(curate hard-won records here — survives regeneration.)_
- [release-drive-2026-08-03](2026/08/release-drive-2026-08-03.md)
- [release-drive-2026-08-07](2026/08/release-drive-2026-08-07.md)
- [release-validation-tri-state](2026/08/release-validation-tri-state.md)
+- [release-version-sync-back-to-main](2026/08/release-version-sync-back-to-main.md)
- [resolve-border-relocator-hazard](2026/08/resolve-border-relocator-hazard.md) — Reconciled the likelihood hazard instrument after the border-relocator source fix. The resolved backend-diverg…
- [resolve-curvature-floor-doc-drift](2026/08/resolve-curvature-floor-doc-drift.md) — Reconciled the curvature-floor documentation finding after PyAutoArray#444. The detector now requires both run…
- [resolve-sersic-ell-comps-gradient](2026/08/resolve-sersic-ell-comps-gradient.md) — Reconciled the numerical-hazard evidence after the Sersic Cartesian-radius fix. The stable conditional detecto…
diff --git a/dashboard.md b/dashboard.md
index 6194623b..f70022b9 100644
--- a/dashboard.md
+++ b/dashboard.md
@@ -17,20 +17,20 @@ Live on GitHub: [open issues](https://github.com/search?q=org%3APyAutoLabs+is%3A
## Start here
-**Highest priority** (filed as `high`) — showing 12 of 29
+**Highest priority** (filed as `high`) — showing 12 of 28
- [TRIAGE: needs manual review before routing](draft/triage/jax_zero_contour.md) — medium · safe · high
- [Decide whether the clipper belongs in the search identifier](draft/feature/autofit/clipper_in_search_identifier.md) — autofit · medium · human-required · high
- [PyAutoLens RTD docs: three-regime restructure (multi_galaxy / group / cluster)](draft/docs/autolens/docs_three_regime_restructure.md) — autolens · medium · supervised · high
- [The `ell_comps` trapping was masked, not cleared — characterise it](draft/research/autolens_profiling/ell_comps_trapping_unmasked.md) — autolens_profiling · medium · supervised · high
- [Optimize pixelized Prodigy settings on the laptop GPU](draft/research/autolens_workspace_developer/pixelized_prodigy_laptop_gpu_phase_2_settings.md) — autolens_workspace_developer · medium · human-required · high
-- [Release does not sync __version__ stamps and workspace pins back](draft/bug/pyautobuild/release_version_sync_back_to_main.md) — pyautobuild · medium · supervised · high
- [Cluster package: point-source-default narrative + extended-source follow-up feature](draft/docs/workspaces/cluster_regime_narrative.md) — workspaces · medium · supervised · high
- [multi_galaxy package: new regime package in autolens_workspace](draft/docs/autolens/multi_galaxy_package.md) — autolens · large · supervised · high
- [Tune the JAX multi-start optimizers into a standard option (MGE](draft/experiment/autolens_profiling/jax_optimizer_settings_tuning.md) — autolens_profiling · large · supervised · high
- [Profile and speed up JAX likelihood-function compile times (all use](draft/feature/autolens_profiling/jax_compile_time_profiling.md) — autolens_profiling · large · supervised · high
- [Optimize MultiStartProdigy for pixelized meshes on the laptop GPU](draft/research/autolens_workspace_developer/pixelized_prodigy_laptop_gpu.md) — autolens_workspace_developer · large · human-required · high
- [Give the Profiling Agent a compile-time axis — the arc](draft/feature/profiling/profiling_agent_jax_compile_time_scope.md) — profiling · large · supervised · high
+- [Deep research: Can we speed up Delaunay in PyAutoArray?](draft/research/autoarray/delaunay_research.md) — autoarray · too-large · supervised · high
**Quick wins** (small enough, and safe enough to run unattended)
@@ -85,9 +85,8 @@ Scoped but not started; some are not yet prompt files. Full detail in [`planned.
**134** filed prompts, not started. Each section is sorted most-pickable first (priority, then size).
-bug — 36
+bug — 35
-- [Release does not sync __version__ stamps and workspace pins back](draft/bug/pyautobuild/release_version_sync_back_to_main.md) — pyautobuild · medium · supervised · high
- [EP hierarchical parent-scale collapse: cure the basin, or document the](draft/bug/autofit/ep_scale_collapse_basin_cure_or_caveat.md) — autofit · too-large · human-required · high
- [`NFWTruncatedSph.potential_2d_from`: MGE potential fails `grad(psi)=alpha` self-consistency](draft/bug/autogalaxy/nfw_truncated_potential_accuracy.md) — autogalaxy · too-large · supervised · high
- [Investigate eager `FitImaging.figure_of_merit` vs JIT/step-by-step divergence in rectangular pixelization](draft/bug/autolens/pixelization_eager_vs_jit_divergence.md) — autolens · too-large · supervised · high
@@ -127,7 +126,7 @@ Scoped but not started; some are not yet prompt files. Full detail in [`planned.
-feature — 26
+feature — 27
- [Decide whether the clipper belongs in the search identifier](draft/feature/autofit/clipper_in_search_identifier.md) — autofit · medium · human-required · high
- [Profile and speed up JAX likelihood-function compile times (all use](draft/feature/autolens_profiling/jax_compile_time_profiling.md) — autolens_profiling · large · supervised · high
@@ -152,6 +151,7 @@ Scoped but not started; some are not yet prompt files. Full detail in [`planned.
- [Context: PyAutoLens issue #542 follow-up (Gap 2, deferred during the](draft/feature/jax_substructure/6_deflection_equivalence_test.md) — jax_substructure · too-large · supervised · normal
- [Once https://github.com/PyAutoLabs/PyAutoLens/issues/480 is fixed (PointSolver](draft/feature/workspaces/restore_multiple_sources_lensing_of_lens.md) — workspaces · too-large · supervised · normal
- [dPIE: optional central-dispersion (sigma_0) parameterization](draft/feature/autogalaxy/dpie_sigma0_parameterization.md) — autogalaxy · small · supervised · low
+- [version_skew: flag a floor that names a PyPI-yanked release](draft/feature/pyautoheart/version_skew_yank_awareness.md) — pyautoheart · small · supervised · low
- [Gallery runner: add visualization_upper + decide the modeling_visualization_jit tier](draft/feature/workspaces/gallery_runner_missing_tiers.md) — workspaces · small · supervised · low
- [Scheduled runs — overnight queue passes with a morning report](draft/feature/autonomy/10_scheduled_runs.md) — autonomy · medium · supervised · low
- [Teach repos_sync --write to stamp organ config surfaces](draft/feature/pyautomind/repos-sync-config-stamper.md) — pyautomind · hard · supervised · low
diff --git a/draft/feature/pyautoheart/version_skew_yank_awareness.md b/draft/feature/pyautoheart/version_skew_yank_awareness.md
new file mode 100644
index 00000000..e09296e3
--- /dev/null
+++ b/draft/feature/pyautoheart/version_skew_yank_awareness.md
@@ -0,0 +1,46 @@
+# version_skew: flag a floor that names a PyPI-yanked release
+
+Type: feature
+Target: PyAutoHeart
+Repos:
+- PyAutoHeart
+Difficulty: small
+Autonomy: supervised
+Priority: low
+Status: formalised
+
+## Why
+
+The `version_skew` Heart leg (reworked under build-chain #155 Phase 4 task 2,
+PyAutoHeart#96) enforces "a floor must name an *installable* release" only
+against **local git tags**: UNSATISFIABLE fires when
+`version.minimum_library_version` exceeds the newest `YYYY.M.D.B` release tag.
+It cannot see the other way a floor goes bad — the release it names being
+**yanked on PyPI afterwards** (as `2026.7.6.649` was; that yank is what
+originally exposed the "floors named a yanked release" bug that this check now
+half-guards). The gap is acknowledged in the check itself
+(`heart/checks/version_skew.py:33`: "a release that was later *yanked* on PyPI —
+that needs the PyPI API, not git tags") and was left unowned when the Phase 4
+tracker (`complete/2026/08/release-version-sync-back-to-main.md`) retired.
+
+## Scope
+
+- Extend `version_skew` (or add a sibling non-tick check, if network access
+ disqualifies it from the tick path — the current check is deliberately
+ local-tags-only, no import/network) to query the PyPI JSON API for the
+ floor's version and flag `yanked: true` per package.
+- Verdict shape should mirror the existing one: a yanked floor is the same
+ class of defect as UNSATISFIABLE (no installable version satisfies "exactly
+ this floor"), but the floor semantics (>=) mean a yanked floor with newer
+ non-yanked releases still resolves — decide whether that is RED, YELLOW, or
+ informational, and record the reasoning.
+- Offline/API-failure behaviour must be UNKNOWN/STALE, never a false RED —
+ match how the tag-based check treats unresolvable repos.
+
+## Constraints
+
+- Do not slow the readiness tick: if the PyPI call cannot be cached or made
+ optional, keep it out of the tick path (nightly / on-demand only).
+- Fork (b) of the version model stands (mains authoritative, floors + tags +
+ wheels as the live signals — see the retired tracker). This check reads
+ state; it must not resurrect any commit-back behaviour.