# BACKLOG — phase `prevb` SSOT: `/srv/cc-ci/cc-ci-plan/plan-phase-prevb-previous-dynamic-base.md`. ## Build backlog ### M1 — implemented + green locally - [ ] B1. Dynamic upgrade-base resolution: last-green (warm canonical registry version) → fallback target-branch (`main`) tip → else skip (declared reason). Replace the static `previous_version(vers[-2])` default in `run_recipe_ci.upgrade_base`. Wire into `main()` deploy. - [ ] B2. `tests//previous/` mechanism: discovery, declared-target-version marker, base-only application (added to base deploy's COMPOSE_FILE), head exclusion (never applied to PR head), version-guard + stale-flag on mismatch. - [ ] B3. Discourse migration: shrink `compose.ccci.yml` to environmental-only (`order: stop-first`), delete bitnamilegacy image pins + sidekiq block; remove `UPGRADE_BASE_VERSION` from `tests/discourse/recipe_meta.py`. (Expect NO `previous/`.) - [ ] B4. Unit tests for the new surface: base resolution (last-green / main-tip / skip), `previous/` match / skip / stale, environmental-vs-version overlay layering. Update `test_upgrade_base.py` to the new resolver API without weakening coverage. - [ ] B5. Discourse upgrade tier GREEN locally: base (bitnamilegacy:3.5.0) → head; assert deployed `app` image == `discourse/discourse:3.5.3` (NOT bitnamilegacy) and no `sidekiq` service post-deploy. - [ ] B6. CLAIM M1 (clean tree + STATUS verification block). ### M2 — proven in real CI + spot-check - [ ] B7. discourse PR #4 `!testme` GREEN in real CI; head ran `discourse/discourse:3.5.3`, migration exercised. - [ ] B8. Spot-check ≥3 other upgrade-tier recipes (warm-canonical / published-predecessor / ex-`.ccci` e.g. keycloak/cryptpad/ghost) still green under dynamic base. Reconcile levels/records. - [ ] B9. CLAIM M2 → `## DONE` after fresh Adversary PASS on M1+M2. ## Adversary findings (Adversary-owned section — Builder does not edit below.)