diff --git a/complete/2026/08/version-skew-yank-awareness.md b/complete/2026/08/version-skew-yank-awareness.md new file mode 100644 index 00000000..8e01c35c --- /dev/null +++ b/complete/2026/08/version-skew-yank-awareness.md @@ -0,0 +1,107 @@ +# version-skew-yank-awareness + +**Completed:** 2026-08-18 +**Type:** feature · **Target:** PyAutoHeart · **PR:** PyAutoHeart#148 (merged, +`7659f153`; branch `feature/version-skew-yank-awareness`) + +## Summary + +Closed the yank gap the `version_skew` Heart leg acknowledged in its own +docstring: the tick check compares floors against **local git tags**, so it +could not see the other way a floor goes bad — the release it names being +**yanked on PyPI** afterwards (the 2026-07 incident shape, where every floor +named the yanked `2026.7.6.649`). Filed as the surviving loose end when the +Phase 4 tracker retired ([[release-version-sync-back-to-main]]); shipped the +same day. + +## What shipped + +`python -m heart.checks.version_skew --pypi` — a deep, on-demand leg that asks +the PyPI JSON API whether each workspace floor still names an installable +(non-yanked) release and whether *any* installable release satisfies it. + +- **Statuses → readiness:** `UNSATISFIABLE` (nothing installable ≥ floor — + every candidate yanked; same defect class as the tag leg) → **RED**; + `FLOOR_YANKED` (floor itself yanked/absent but newer installable releases + satisfy it — floors are `>=` bounds so installs still resolve) → **YELLOW** + "bump the floor"; `UNKNOWN` (PyPI unreachable) → **STALE**, never a false + block; `OK`/`BAD` as in the tag leg. +- **Tick untouched:** network-bound, so never run from `tick.sh` — on-demand / + nightly only, behind the explicit `--pypi` flag. +- **Sidecar state** (`version_skew_pypi.json`): the tick's `version_skew.json` + rewrite can never clobber on-demand PyPI evidence, and vice versa. An absent + slice is no signal in readiness and the dashboard. +- `run_pypi()` side-effect-free like `run()` (persistence in `main()` only); + one PyPI fetch per distinct package, not per workspace. Wired through + `state.py` snapshot, readiness legs + score weights, and a "Version skew + (PyPI)" dashboard section. +- **Validated:** 484 tests pass (15 new); live probe against real PyPI + (`autolens`, 421 releases): `2026.7.9.1 → OK`, the incident release + `2026.7.6.649 → FLOOR_YANKED`, `2099.1.1.1 → UNSATISFIABLE`, + `garbage → BAD`. + +## Key findings / traps + +- **Tenant firewall caught a real leak on the first CI round:** the + one-fetch-per-package test named `HowToLens` — a *new* instance fact in + organ code (`repos_sync.py --check --only "tenant firewall (organ code)"`). + Fixed by using `autolens_assistant` (an already-present fact in that file, + same `autolens` package mapping). When testing organ code, pick instance + names the file already carries; the firewall treats new ones as drift even + in tests. +- **Verdict-shape reasoning recorded in the check itself:** a yanked floor + with newer installable releases is deliberately YELLOW, not RED — `>=` + semantics mean installs still resolve; only "nothing installable ≥ floor" + blocks. Offline degrades to UNKNOWN/STALE so an offline dev box can never + produce a false RED. +- Fork (b) of the version model stands: this reads state only — no + commit-back behaviour was added anywhere. + +## Original prompt + +# 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. diff --git a/complete/index.md b/complete/index.md index a202c88e..2159725e 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. -1022 records across 7 buckets. +1023 records across 7 buckets. ## Highlights @@ -145,6 +145,7 @@ _(curate hard-won records here — survives regeneration.)_ - [test-mode-fit-exception-finalization](2026/08/test-mode-fit-exception-finalization.md) — `PYAUTO_TEST_MODE=1` no longer finalizes a model point whose reconstruction raises `FitException`; it substitu… - [test-mode-samples-info-hook-contract](2026/08/test-mode-samples-info-hook-contract.md) — `NonLinearSearch._test_mode_samples_info()`'s docstring told subclasses - [uniform-prior-bounds-numpy-path](2026/08/uniform-prior-bounds-numpy-path.md) — auto-closed by the merge's `Closes` line +- [version-skew-yank-awareness](2026/08/version-skew-yank-awareness.md) - [wiki-provenance-restamp](2026/08/wiki-provenance-restamp.md) - [worktree-claim-parser-forms](2026/08/worktree-claim-parser-forms.md) — CLOSED completed - [yang24-sidm-gravothermal-profile](2026/08/yang24-sidm-gravothermal-profile.md) diff --git a/dashboard.md b/dashboard.md index 00e22817..37d23bfb 100644 --- a/dashboard.md +++ b/dashboard.md @@ -11,7 +11,7 @@ Tasks only — the organism's health lives with the Heart (`/health`), not here. | [In flight](#in-flight) (`active/`) | 3 | | [Parked](#parked) (`parked.md`) | 1 | | [Planned](#planned) (`planned.md`) | 7 | -| [Backlog](#backlog) (`draft/`) | 128 | +| [Backlog](#backlog) (`draft/`) | 127 | Live on GitHub: [open issues](https://github.com/search?q=org%3APyAutoLabs+is%3Aissue+is%3Aopen&type=issues) · [open pull requests](https://github.com/search?q=org%3APyAutoLabs+is%3Apr+is%3Aopen&type=prs) @@ -79,7 +79,7 @@ Scoped but not started; some are not yet prompt files. Full detail in [`planned. ## Backlog -**128** filed prompts, not started. Each section is sorted most-pickable first (priority, then size). +**127** filed prompts, not started. Each section is sorted most-pickable first (priority, then size).
bug — 35 @@ -123,7 +123,7 @@ Scoped but not started; some are not yet prompt files. Full detail in [`planned.
-feature — 27 +feature — 26 - [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 @@ -148,7 +148,6 @@ 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 deleted file mode 100644 index e09296e3..00000000 --- a/draft/feature/pyautoheart/version_skew_yank_awareness.md +++ /dev/null @@ -1,46 +0,0 @@ -# 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.