feat(canon): §2.G strip UPGRADE_BASE_VERSION entirely (plausible verified dynamic-base green)
All checks were successful
continuous-integration/drone/push Build is passing

Gate satisfied — live: with the pin removed, plausible's upgrade tier resolves base 3.0.1+v2.0.0 via
the same-version step-back (canonical 3.1.0 == head 3.1.0 → newest-older = 3.0.1, NOT the broken
3.0.0) and passes install+upgrade green (level 5/5). The pin is redundant, so removed everywhere:
- meta.py KEYS entry (RecipeMeta field auto-drops; 15→14 keys).
- run_recipe_ci.resolve_upgrade_base override branch + docstrings.
- tests/unit/test_meta.py (count 15→14, dropped None-assert), test_upgrade_base.py (override test).
- docs/recipe-customization.md (regenerated table + mentions), docs/testing.md.
- tests/plausible/recipe_meta.py (pin removed), tests/bluesky-pds (re-enable note → dynamic base).
294 unit tests pass; lint clean.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
autonomic-bot
2026-06-17 12:31:53 +00:00
parent f611dda893
commit 83c183d985
7 changed files with 28 additions and 52 deletions

View File

@ -58,7 +58,6 @@ def test_missing_meta_yields_spec_baseline(tmp_path):
assert meta.BACKUP_CAPABLE is None # None = auto-detect (tri-state, not False)
assert meta.EXPECTED_NA is None
assert meta.READY_PROBE is None
assert meta.UPGRADE_BASE_VERSION is None
assert meta.BACKUP_VERIFY is None
assert meta.UPGRADE_EXTRA_ENV is None
assert meta.EXTRA_ENV == {}
@ -74,9 +73,9 @@ def test_registry_field_set_matches_dataclass():
import dataclasses
assert [f.name for f in dataclasses.fields(RecipeMeta)] == [k.name for k in KEYS]
# the 15 final keys, no more (the 3 P2-deleted legacy keys are gone from the registry,
# so any recipe_meta still setting them hard-fails the typo gate)
assert len(KEYS) == 15
# 14 final keys (UPGRADE_BASE_VERSION removed in phase canon §2.G; the 3 P2-deleted legacy keys
# are gone too — any recipe_meta still setting a removed key hard-fails the typo gate)
assert len(KEYS) == 14
assert not [k for k in KEYS if k.deprecated]
for gone in ("CHAOS_BASE_DEPLOY", "OIDC_AT_INSTALL", "SKIP_GENERIC"):
assert gone not in {k.name for k in KEYS}

View File

@ -1,9 +1,10 @@
"""Unit tests for `run_recipe_ci.resolve_upgrade_base` — the DYNAMIC upgrade-base decision (phase prevb).
Resolution order: upgrade∉stages / EXPECTED_NA[upgrade] (declared skip) → explicit UPGRADE_BASE_VERSION
override → last-green (warm canonical) → target-branch (`main`) tip → skip (no predecessor). The result
is a `BasePlan(kind, version, ref, reason)`: kind ∈ {"version", "ref", "skip"}; `.runs` is True for
version/ref (the upgrade tier runs). Replaces the old static `recipe_versions[-2]` default.
Resolution order: upgrade∉stages / EXPECTED_NA[upgrade] (declared skip) → last-green (warm canonical,
with same-version step-back) → target-branch (`main`) tip → skip (no predecessor). The result is a
`BasePlan(kind, version, ref, reason)`: kind ∈ {"version", "ref", "skip"}; `.runs` is True for
version/ref (the upgrade tier runs). Replaces the old static `recipe_versions[-2]` default; the old
explicit `UPGRADE_BASE_VERSION` override knob was removed in phase canon (§2.G).
"""
from __future__ import annotations
@ -22,8 +23,8 @@ HEAD = "aaaa1111head"
MAIN = "bbbb2222main"
def _meta(expected_na=None, upgrade_base_version=None):
return SimpleNamespace(EXPECTED_NA=expected_na, UPGRADE_BASE_VERSION=upgrade_base_version)
def _meta(expected_na=None):
return SimpleNamespace(EXPECTED_NA=expected_na)
def _no_canonical(monkeypatch):
@ -43,33 +44,16 @@ def test_upgrade_not_in_stages_skip(monkeypatch):
assert plan.kind == "skip" and not plan.runs
def test_expected_na_upgrade_skip_even_with_canonical_and_override(monkeypatch):
# EXPECTED_NA[upgrade] is the strongest, declared fact — short-circuits before override/canonical.
def test_expected_na_upgrade_skip_even_with_canonical(monkeypatch):
# EXPECTED_NA[upgrade] is the strongest, declared fact — short-circuits before canonical.
monkeypatch.setattr(
canonical, "read_registry", lambda r: {"version": "9.9.9", "status": "warm"}
)
declared = {"upgrade": "no deployable upgrade base (moving tag republished)"}
plan = run_recipe_ci.resolve_upgrade_base(
ALL, _meta(expected_na=declared, upgrade_base_version="0.2.0+v0.4"), "bluesky-pds"
)
plan = run_recipe_ci.resolve_upgrade_base(ALL, _meta(expected_na=declared), "bluesky-pds")
assert plan.kind == "skip" and "EXPECTED_NA" in plan.reason
def test_explicit_override_wins_over_canonical(monkeypatch):
monkeypatch.setattr(
canonical, "read_registry", lambda r: {"version": "9.9.9", "status": "warm"}
)
monkeypatch.setattr(
lifecycle,
"recipe_branch_commit",
lambda r, b="main": (_ for _ in ()).throw(AssertionError("not consulted")),
)
plan = run_recipe_ci.resolve_upgrade_base(
ALL, _meta(upgrade_base_version="0.7.0+3.3.1"), "discourse"
)
assert plan.kind == "version" and plan.version == "0.7.0+3.3.1" and plan.runs
def test_last_green_warm_canonical_is_primary(monkeypatch):
# no override → last-green (warm canonical version) is the PRIMARY base; main is not consulted.
monkeypatch.setattr(