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>
91 lines
5.2 KiB
Markdown
91 lines
5.2 KiB
Markdown
# 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_upgrade` → `lifecycle.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_restore` → `backup_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_count` — `run_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.)
|