Skip to content

Commit 5125841

Browse files
committed
prompt: record version-skew-yank-awareness shipped (PyAutoHeart#148)
The deep `version_skew --pypi` leg closes the yank gap left when the release-version-sync Phase 4 tracker retired: floors are now checked against PyPI installability (UNSATISFIABLE=RED, FLOOR_YANKED=YELLOW, offline=STALE), on-demand only with a sidecar state file so the tick stays network-free. Draft prompt retired to the dated record. Co-Authored-By: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01SKw8oaLZoD3cZnmETMDEkc
1 parent 5eb2258 commit 5125841

4 files changed

Lines changed: 112 additions & 51 deletions

File tree

Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,107 @@
1+
# version-skew-yank-awareness
2+
3+
**Completed:** 2026-08-18
4+
**Type:** feature · **Target:** PyAutoHeart · **PR:** PyAutoHeart#148 (merged,
5+
`7659f153`; branch `feature/version-skew-yank-awareness`)
6+
7+
## Summary
8+
9+
Closed the yank gap the `version_skew` Heart leg acknowledged in its own
10+
docstring: the tick check compares floors against **local git tags**, so it
11+
could not see the other way a floor goes bad — the release it names being
12+
**yanked on PyPI** afterwards (the 2026-07 incident shape, where every floor
13+
named the yanked `2026.7.6.649`). Filed as the surviving loose end when the
14+
Phase 4 tracker retired ([[release-version-sync-back-to-main]]); shipped the
15+
same day.
16+
17+
## What shipped
18+
19+
`python -m heart.checks.version_skew --pypi` — a deep, on-demand leg that asks
20+
the PyPI JSON API whether each workspace floor still names an installable
21+
(non-yanked) release and whether *any* installable release satisfies it.
22+
23+
- **Statuses → readiness:** `UNSATISFIABLE` (nothing installable ≥ floor —
24+
every candidate yanked; same defect class as the tag leg) → **RED**;
25+
`FLOOR_YANKED` (floor itself yanked/absent but newer installable releases
26+
satisfy it — floors are `>=` bounds so installs still resolve) → **YELLOW**
27+
"bump the floor"; `UNKNOWN` (PyPI unreachable) → **STALE**, never a false
28+
block; `OK`/`BAD` as in the tag leg.
29+
- **Tick untouched:** network-bound, so never run from `tick.sh` — on-demand /
30+
nightly only, behind the explicit `--pypi` flag.
31+
- **Sidecar state** (`version_skew_pypi.json`): the tick's `version_skew.json`
32+
rewrite can never clobber on-demand PyPI evidence, and vice versa. An absent
33+
slice is no signal in readiness and the dashboard.
34+
- `run_pypi()` side-effect-free like `run()` (persistence in `main()` only);
35+
one PyPI fetch per distinct package, not per workspace. Wired through
36+
`state.py` snapshot, readiness legs + score weights, and a "Version skew
37+
(PyPI)" dashboard section.
38+
- **Validated:** 484 tests pass (15 new); live probe against real PyPI
39+
(`autolens`, 421 releases): `2026.7.9.1 → OK`, the incident release
40+
`2026.7.6.649 → FLOOR_YANKED`, `2099.1.1.1 → UNSATISFIABLE`,
41+
`garbage → BAD`.
42+
43+
## Key findings / traps
44+
45+
- **Tenant firewall caught a real leak on the first CI round:** the
46+
one-fetch-per-package test named `HowToLens` — a *new* instance fact in
47+
organ code (`repos_sync.py --check --only "tenant firewall (organ code)"`).
48+
Fixed by using `autolens_assistant` (an already-present fact in that file,
49+
same `autolens` package mapping). When testing organ code, pick instance
50+
names the file already carries; the firewall treats new ones as drift even
51+
in tests.
52+
- **Verdict-shape reasoning recorded in the check itself:** a yanked floor
53+
with newer installable releases is deliberately YELLOW, not RED — `>=`
54+
semantics mean installs still resolve; only "nothing installable ≥ floor"
55+
blocks. Offline degrades to UNKNOWN/STALE so an offline dev box can never
56+
produce a false RED.
57+
- Fork (b) of the version model stands: this reads state only — no
58+
commit-back behaviour was added anywhere.
59+
60+
## Original prompt
61+
62+
# version_skew: flag a floor that names a PyPI-yanked release
63+
64+
Type: feature
65+
Target: PyAutoHeart
66+
Repos:
67+
- PyAutoHeart
68+
Difficulty: small
69+
Autonomy: supervised
70+
Priority: low
71+
Status: formalised
72+
73+
## Why
74+
75+
The `version_skew` Heart leg (reworked under build-chain #155 Phase 4 task 2,
76+
PyAutoHeart#96) enforces "a floor must name an *installable* release" only
77+
against **local git tags**: UNSATISFIABLE fires when
78+
`version.minimum_library_version` exceeds the newest `YYYY.M.D.B` release tag.
79+
It cannot see the other way a floor goes bad — the release it names being
80+
**yanked on PyPI afterwards** (as `2026.7.6.649` was; that yank is what
81+
originally exposed the "floors named a yanked release" bug that this check now
82+
half-guards). The gap is acknowledged in the check itself
83+
(`heart/checks/version_skew.py:33`: "a release that was later *yanked* on PyPI —
84+
that needs the PyPI API, not git tags") and was left unowned when the Phase 4
85+
tracker (`complete/2026/08/release-version-sync-back-to-main.md`) retired.
86+
87+
## Scope
88+
89+
- Extend `version_skew` (or add a sibling non-tick check, if network access
90+
disqualifies it from the tick path — the current check is deliberately
91+
local-tags-only, no import/network) to query the PyPI JSON API for the
92+
floor's version and flag `yanked: true` per package.
93+
- Verdict shape should mirror the existing one: a yanked floor is the same
94+
class of defect as UNSATISFIABLE (no installable version satisfies "exactly
95+
this floor"), but the floor semantics (>=) mean a yanked floor with newer
96+
non-yanked releases still resolves — decide whether that is RED, YELLOW, or
97+
informational, and record the reasoning.
98+
- Offline/API-failure behaviour must be UNKNOWN/STALE, never a false RED —
99+
match how the tag-based check treats unresolvable repos.
100+
101+
## Constraints
102+
103+
- Do not slow the readiness tick: if the PyPI call cannot be cached or made
104+
optional, keep it out of the tick path (nightly / on-demand only).
105+
- Fork (b) of the version model stands (mains authoritative, floors + tags +
106+
wheels as the live signals — see the retired tracker). This check reads
107+
state; it must not resurrect any commit-back behaviour.

‎complete/index.md‎

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ Token-light navigation over the finished-work records (schema:
66
only then grep a dated bucket. Curators: edit the band between the CURATED
77
markers; everything below GENERATED is rebuilt.
88

9-
1022 records across 7 buckets.
9+
1023 records across 7 buckets.
1010

1111
<!-- CURATED:START -->
1212
## Highlights
@@ -145,6 +145,7 @@ _(curate hard-won records here — survives regeneration.)_
145145
- [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…
146146
- [test-mode-samples-info-hook-contract](2026/08/test-mode-samples-info-hook-contract.md) — `NonLinearSearch._test_mode_samples_info()`'s docstring told subclasses
147147
- [uniform-prior-bounds-numpy-path](2026/08/uniform-prior-bounds-numpy-path.md) — auto-closed by the merge's `Closes` line
148+
- [version-skew-yank-awareness](2026/08/version-skew-yank-awareness.md)
148149
- [wiki-provenance-restamp](2026/08/wiki-provenance-restamp.md)
149150
- [worktree-claim-parser-forms](2026/08/worktree-claim-parser-forms.md) — CLOSED completed
150151
- [yang24-sidm-gravothermal-profile](2026/08/yang24-sidm-gravothermal-profile.md)

‎dashboard.md‎

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ Tasks only — the organism's health lives with the Heart (`/health`), not here.
1111
| [In flight](#in-flight) (`active/`) | 3 |
1212
| [Parked](#parked) (`parked.md`) | 1 |
1313
| [Planned](#planned) (`planned.md`) | 7 |
14-
| [Backlog](#backlog) (`draft/`) | 128 |
14+
| [Backlog](#backlog) (`draft/`) | 127 |
1515

1616
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)
1717

@@ -79,7 +79,7 @@ Scoped but not started; some are not yet prompt files. Full detail in [`planned.
7979

8080
## Backlog
8181

82-
**128** filed prompts, not started. Each section is sorted most-pickable first (priority, then size).
82+
**127** filed prompts, not started. Each section is sorted most-pickable first (priority, then size).
8383

8484
<details>
8585
<summary><b>bug</b> — 35</summary>
@@ -123,7 +123,7 @@ Scoped but not started; some are not yet prompt files. Full detail in [`planned.
123123
</details>
124124

125125
<details>
126-
<summary><b>feature</b> — 27</summary>
126+
<summary><b>feature</b> — 26</summary>
127127

128128
- [Decide whether the clipper belongs in the search identifier](draft/feature/autofit/clipper_in_search_identifier.md) — autofit · medium · human-required · high
129129
- [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.
148148
- [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
149149
- [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
150150
- [dPIE: optional central-dispersion (sigma_0) parameterization](draft/feature/autogalaxy/dpie_sigma0_parameterization.md) — autogalaxy · small · supervised · low
151-
- [version_skew: flag a floor that names a PyPI-yanked release](draft/feature/pyautoheart/version_skew_yank_awareness.md) — pyautoheart · small · supervised · low
152151
- [Gallery runner: add visualization_upper + decide the modeling_visualization_jit tier](draft/feature/workspaces/gallery_runner_missing_tiers.md) — workspaces · small · supervised · low
153152
- [Scheduled runs — overnight queue passes with a morning report](draft/feature/autonomy/10_scheduled_runs.md) — autonomy · medium · supervised · low
154153
- [Teach repos_sync --write to stamp organ config surfaces](draft/feature/pyautomind/repos-sync-config-stamper.md) — pyautomind · hard · supervised · low

‎draft/feature/pyautoheart/version_skew_yank_awareness.md‎

Lines changed: 0 additions & 46 deletions
This file was deleted.

0 commit comments

Comments
 (0)