docs+catalog: bitcoin multi-version rollout handoff + reproducible generator

- generate-app-catalog.sh: VERSIONS map now lists the full Knots set
  (29.3.knots20260508/20260507/20260210 + 29.2.knots20251110) and Core
  (adds 29.2 + a `latest` entry → newest); generator forces top-level
  `version` == the default entry's version (the 169ff2e2 invariant) so
  regeneration is reproducible. releases/app-catalog.json regenerated.
- docs/bitcoin-version-bulletproof-rollout.md: full handoff — root causes,
  fixes, current .228 state, the coordinated fleet-rollout steps (incl.
  :latest repoint sequencing / fleet-safety), reindex finish procedure, and
  the switch-matrix test plan.
- PRODUCTION-MASTER-PLAN.md: link the rollout doc (§6b-bis).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
archipelago
2026-06-29 06:02:24 -04:00
co-authored by Claude Opus 4.8
parent 095a76cd20
commit ed1352d3a3
4 changed files with 166 additions and 4 deletions
+23 -2
View File
@@ -182,10 +182,12 @@ VERSIONS = {
# "Version & Updates" card. Add the next release by building its image then
# prepending it here.
"bitcoin-core": [
{"version": "latest", "image": f"{REGISTRY}/bitcoin:latest", "default": True},
{"version": "31.0", "image": f"{REGISTRY}/bitcoin:31.0"},
{"version": "30.2", "image": f"{REGISTRY}/bitcoin:30.2"},
{"version": "29.3", "image": f"{REGISTRY}/bitcoin:29.3"},
{"version": "28.4.0", "image": f"{REGISTRY}/bitcoin:28.4", "default": True},
{"version": "29.2", "image": f"{REGISTRY}/bitcoin:29.2"},
{"version": "28.4.0", "image": f"{REGISTRY}/bitcoin:28.4"},
{"version": "27.2", "image": f"{REGISTRY}/bitcoin:27.2"},
{"version": "26.2", "image": f"{REGISTRY}/bitcoin:26.2", "deprecated": True},
{"version": "25.2", "image": f"{REGISTRY}/bitcoin:25.2", "deprecated": True},
@@ -196,16 +198,35 @@ VERSIONS = {
# top-level catalog version (L167-168) or the card can't reach "latest" and
# selecting the highlighted default would instead pin+recreate. Pinning
# 29.3.knots20260508 moves a runner off the floating tag.
# `latest` is the default and points at the NEWEST published dated image
# (not the bare :latest tag) so "Always use the latest version" installs the
# newest build on fixed-binary nodes, while UNPINNED nodes still resolve via
# the manifest's floating :latest tag (kept on the legacy image until the
# entrypoint-render fix is fleet-deployed — see
# docs/bitcoin-version-bulletproof-rollout.md).
"bitcoin-knots": [
{"version": "latest",
"image": f"{REGISTRY}/bitcoin-knots:latest", "default": True},
"image": f"{REGISTRY}/bitcoin-knots:29.3.knots20260508", "default": True},
{"version": "29.3.knots20260508",
"image": f"{REGISTRY}/bitcoin-knots:29.3.knots20260508"},
{"version": "29.3.knots20260507",
"image": f"{REGISTRY}/bitcoin-knots:29.3.knots20260507"},
{"version": "29.3.knots20260210",
"image": f"{REGISTRY}/bitcoin-knots:29.3.knots20260210"},
{"version": "29.2.knots20251110",
"image": f"{REGISTRY}/bitcoin-knots:29.2.knots20251110"},
],
}
for app_id, versions in VERSIONS.items():
if app_id in apps and versions:
apps[app_id]["versions"] = versions
# The default/latest entry MUST equal the app's top-level catalog
# `version` (commit 169ff2e2) so selecting the highlighted default
# un-pins / tracks latest instead of pinning+recreating. Enforce it here
# rather than relying on the manifest version matching.
default_entry = next((v for v in versions if v.get("default")), None)
if default_entry:
apps[app_id]["version"] = default_entry["version"]
catalog = {
"schema": 1,