Files
cc-ci/docs/perf/deploys.md
autonomic-bot edf34e3e53 claim(2b): deploy budget confirmed minimal+enforced (1+N_cold_deps); B1-B4 claimed
Phase 2b confirm-and-document outcome: per-recipe test-sequence deploy budget is
already minimal — `deploys == 1 (base, shared by all 5 tiers) + N_cold_deps` — and
tighter than plan B1's nominal `1+1(upgrade)+N` because the upgrade is an in-place
chaos redeploy of the prev-version base, not a separate deploy. Enforced as a hard
failure by DG4.1 (expected = 1 + deps_deployed_count, run_recipe_ci.py:1005-1010).
No redundant deploy found; none removed (none existed).

- docs/perf/deploys.md: the budget record (B4), names the out-of-budget WC5 reseed
- STATUS-2b.md: B1-B4 claim with WHAT/HOW/EXPECTED/WHERE for cold verify
- JOURNAL-2b.md / BACKLOG-2b.md / DECISIONS.md: reasoning + settled note
- consume machine-docs/BUILDER-INBOX.md (Adversary heads-up processed)

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-05-31 05:35:46 +00:00

5.2 KiB

Per-recipe deploy budget (Phase 2b)

Question: does a recipe's full CI test sequence redeploy more than necessary? Answer: No. The budget is already minimal — and in fact tighter than the nominal 1 base + 1 upgrade + N_deps — because the upgrade tier shares the base deployment.

The budget

For one cold !testme/run_recipe_ci.py run of a recipe:

deploys == 1 (base) + N_cold_deps
  • 1 base deploy, shared by install → upgrade → backup → restore → custom/functional. All five tiers run against this single deployment. (run_recipe_ci.py:819, lifecycle.deploy_app_record_deploy.)
  • + 1 per COLD declared dependency (e.g. an SSO provider deployed in-run), each deployed once and reused (deps.py:81-120, one deploy_app per dep). A live-warm dep (e.g. a resident keycloak that only gets a per-run realm, not a fresh deploy) contributes 0.
  • The upgrade tier adds NO deploy. When the upgrade tier runs, the base deploy is done at the previous published version (run_recipe_ci.py:746-754: base = prev or target), and the upgrade is an in-place abra app deploy --chaos redeploy of the PR-head code onto that same running app (generic.perform_upgradelifecycle.chaos_redeploy). chaos_redeploy does not call deploy_app, so it is not counted — and it is the real upgrade the PR's changes are exercised by (HC1), verified by assert_upgraded on the chaos-version label.
  • backup and restore add NO deploy. They operate on the same running app (perform_backup/perform_restorebackup_app/restore_app); neither calls deploy_app.

Reconciliation with the plan's nominal budget

Plan B1 states the nominal minimum as 1 (base) + 1 (upgrade tier) + N_deps, assuming the upgrade tier needs its own prior-version deploy. The cc-ci design is stricter: the base deploy is the prior-version deploy (when upgrade runs), and the upgrade is performed in place. So the prior-version deploy and the base deploy are the same deploy — there is no separate upgrade deploy. Net actual budget: 1 + N_cold_deps. This is the deploy-sharing the operator expected.

Enforcement (not just claimed)

The harness counts every deploy_app() (the only caller of _record_deploy, lifecycle.py:107-211) into a per-run countfile and hard-fails on a mismatch:

  • expected_deploy_count = 1 + deps_deployed_countrun_recipe_ci.py:984 (deps_deployed_count excludes warm deps, :982-983).
  • RUN SUMMARY prints deploy-count = N (expect M)run_recipe_ci.py:986.
  • if deploy_count != expected_deploy_count: … overall = 1 (DG4.1 violation, non-zero exit) — run_recipe_ci.py:1005-1010.

So every green run is a proof that the recipe stayed within budget: a redundant redeploy would push deploy_count above expected and turn the run red. No recipe can silently exceed the budget.

Verify from a cold clone

RECIPE=ghost        STAGES=install,upgrade,backup,restore,custom  cc-ci-run runner/run_recipe_ci.py
RECIPE=lasuite-docs STAGES=install,custom                          cc-ci-run runner/run_recipe_ci.py

Expected RUN SUMMARY lines:

  • no-dep recipe (ghost): deploy-count = 1 (expect 1), all tiers pass.
  • cold-dep recipe (lasuite-docs + cold keycloak): deploy-count = 2 (expect 2)deps deployed: ['keycloak'] — all tiers pass, DEPS teardown clean.
  • warm-dep recipe (lasuite-meet, live-warm keycloak): deploy-count = 1 (expect 1), deps deployed: ['keycloak'].

Observed across all Phase 2 recipe runs: every recipe ran at deploy-count = 1 (no/warm deps) or deploy-count = 2 (expect 2) (one cold dep). No run exceeded 1 + N_cold_deps.

No test weakened to share the deploy

Sharing one deployment does not skip or soften any check:

  • install, upgrade, backup, restore, custom each still run their real generic + overlay assertions against the shared app (run_lifecycle_tier, ALL_STAGES).
  • the upgrade is a real prev→PR-head crossover (assert_upgraded on the chaos-version label), not a no-op.
  • backup→restore is real data-integrity (P4: seed → backup → mutate → restore → assert the seeded data survived), not health-only.
  • per-run isolation/teardown is unchanged (DEPS teardown, app undeploy, volume/secret cleanup).

Only the deploy count is constrained; coverage is untouched.

Out of scope of the budget (intentionally)

  • WC5 canonical promote (promote_canonical, run_recipe_ci.py:682-707) deploys a separate warm-<recipe> app to (re)seed the warm-cache canonical. It runs only on a green cold run on LATEST, after the deploy-count assertion, and explicitly pops CCCI_DEPLOY_COUNT_FILE (:697) so it does not perturb the per-run test budget. It is warm-cache maintenance, not a test deploy.
  • --quick fast lane (run_quick) reuses an existing data-warm canonical and is a separate optimization path; the cold full run above is the budget of record.

Conclusion

The per-recipe deploy budget is already minimal and enforced: 1 + N_cold_deps, with the upgrade tier sharing the base deploy in place. No redundant deploy was found; none was removed because none existed. (Phase 2b, 2026-05-31.)