Files
cc-ci/machine-docs/STATUS-samever.md
autonomic-bot 398f559168
All checks were successful
continuous-integration/drone/push Build is passing
status(samever): M1 PASS recorded; M2 in progress (custom-html two-run on cc-ci)
2026-06-17 04:32:51 +00:00

5.2 KiB
Raw Blame History

STATUS — phase samever (step-back to older base when canonical == head version)

SSOT: /srv/cc-ci/cc-ci-plan/plan-phase-samever-older-base-fallback.md. State files: this + BACKLOG-samever.md, REVIEW-samever.md (Adversary), JOURNAL-samever.md. DECISIONS.md shared.

Phase

Started 2026-06-17. Gates: M1 (implemented + unit-tested), M2 (proven in real CI).

Current status

M1: PASS (REVIEW-samever.md @2026-06-17T04:27Z — cold-verified, teeth hold, no regression). M2: IN PROGRESS — real-CI demonstration on cc-ci. Plan: custom-html cold-on-latest twice (run A promotes canonical→latest 1.13.0+1.31.1; run B = nightly steady state → step-back to 1.11.0+1.29.0, base<latest); PR form (head==canonical → step-back); version-bump UNAFFECTED (canonical older→head); discourse #4 unaffected; spot-check.

M1 — WHAT is claimed

resolve_upgrade_base now reads the head's published version and steps back to a genuinely older published base when the last-green warm-canonical version equals the head version — never a same-version no-op, never a needless skip when an older base exists.

Resolution chain (override / EXPECTED_NA / upgrade∉stages short-circuits unchanged):

  1. explicit UPGRADE_BASE_VERSION override → unchanged.
  2. last-green canonical IF its version ≠ head versionkind="version" (canonical), unchanged from prevb.
  3. last-green canonical == head versionstep back: newest published version strictly older than headkind="version" (the older tag). Reason starts "step-back: …".
  4. canonical == head and no older published tagkind="skip", reason "base == head (<v>) and no older published predecessor".
  5. no canonical → main-tip ref / skip paths unchanged. head_version is None (compose unreadable) → comparison is False → canonical stays primary (prevb behavior).

M1 — WHERE (commit + paths)

  • Implementation commit: b29bb3f (feat(samever): …), on main.
  • runner/run_recipe_ci.pyresolve_upgrade_base(..., head_version=None) new chain (canonical block ~lines 147180); call site main() reads head_version = abra.head_compose_version(recipe) (~line 1023) and passes it.
  • runner/harness/abra.pyhead_compose_version(recipe) (regex coop-cloud\.[^.\s]*\.version=([^\s"']+) over the head checkout's compose.yml; matches quoted + unquoted labels; does NOT match .chaos-version).
  • runner/warm_reconcile.pyversion_key(tag) (lifted from sort_versions; single ordering source)
    • newest_older_version(tags, version) (newest tag with version_key < target; None if none / version None).
  • tests/unit/test_upgrade_base.py — 5 new tests (13 total).

M1 — HOW to verify (cold, from a clean clone)

  1. Unit suite (the gate):

    nix shell nixpkgs#python311Packages.pytest -c pytest tests/unit/test_upgrade_base.py -v
    

    EXPECTED: 13 passed. New tests:

    • test_canonical_equals_head_steps_back_to_newest_older — canonical==head==10.8.0+26.6.3, tags [10.6.0+26.5.0, 10.8.0+26.6.3, 10.7.1+26.6.2, 10.7.0+26.6.0, not-a-version]plan.version == "10.7.1+26.6.2" (strictly older; asserts version_key(plan.version) < version_key(head)), kind=="version", reason contains "step-back". main never consulted.
    • test_canonical_differs_from_head_uses_canonical_unchanged — canonical 10.7.1+26.6.2 ≠ head 10.8.0+26.6.3version==10.7.1+26.6.2, reason "last-green"; recipe_tags NOT consulted.
    • test_canonical_equals_head_no_older_published_skips — canonical==head==1.0.0+3.5.3, tags [1.0.0+3.5.3] only → kind=="skip", reason contains "no older published predecessor".
    • test_no_head_version_preserves_canonical_primary — head_version omitted → canonical primary, no step-back.
    • test_newest_older_version_ordering — ordering helper picks correct strictly-older tag, excludes equal, None-safe. The 8 prior tests (override / EXPECTED_NA / main-tip / head==main-tip skip / no-predecessor skip / other-rung) are UNCHANGED and still pass — proving override/ref/skip paths untouched.
  2. Teeth (canonical==head MUST NOT yield a same-version base): in test_canonical_equals_head_steps_back_to_newest_older, plan.version != head_version and the version_key(plan.version) < version_key(head) assertion fails loudly if the resolver ever returns the same version or a newer one.

  3. Compose-label parse (the head-version reader): the regex extracts 10.8.0+26.6.3 from a quoted label and 3.5.3+1.24.2-rootless from an unquoted one, and returns no match for a .chaos-version label (verified — see JOURNAL). Real labels confirmed on cc-ci: keycloak 10.8.0+26.6.3, gitea 3.5.3+1.24.2-rootless, discourse 1.0.0+3.5.3.

  4. F1d-2: the step-back returns kind="version", so it flows through the SAME pinned-tag deploy path as a normal canonical base (abra.recipe_checkout pins the tag on disk) — no new deploy code.

Note (pre-existing, NOT introduced by this gate): tests/unit/test_meta.py::test_generated_doc_table_in_sync and tests/unit/test_warm_reconcile.py::test_traefik_spec_is_stateless_with_setup fail on clean 279d84d too (verified by stashing my changes). Out of scope for samever.

Blocked

(none)