chore(1e): bootstrap Phase 1e loop state + settle HC1/HC2/HC3 decisions

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-05-28 02:53:22 +01:00
parent f9257fc891
commit 0226167b49
4 changed files with 127 additions and 0 deletions

View File

@ -332,3 +332,50 @@ SSOT: `cc-ci-plan/plan-phase1d-generic-test-suite.md`. Resolves the §6 open dec
recipe repo's `tests/` (snapshotted after fetch, per the existing volatile-checkout handling).
Generic tier files live in `tests/_generic/` (assertion-only, use the shared live-deployment
fixtures).
---
## Phase 1e — generic-harness corrections (HC1HC4)
Three operator-review corrections to the Phase-1d shared harness, settled here (plan §5).
- **HC2 — repo-local approval allowlist (form/location + workflow).** PR-author-controlled code
(`install_steps.sh`, repo-local `test_*.py`) runs on the CI host with `/run/secrets/*` present, so it
is **default-deny**. Allowlist file: **`tests/repo-local-approved.txt`** (checked into the cc-ci
repo, git-auditable). Format: one recipe name per line; `#` comments + blank lines ignored; a lone
`*` is NOT a wildcard (no global opt-in — every recipe is explicit). **Default: empty ⇒ no recipe
trusts repo-local code.** Discovery (`resolve_op`/`custom_tests`/`install_steps`) consults the
repo-local source **only** when `repo_local_approved(recipe)` is true; otherwise precedence is
**cc-ci > generic** only and repo-local is discovered-but-not-executed. **Workflow:** a cc-ci
maintainer reviews a recipe's repo-local tests, then adds the recipe name to
`tests/repo-local-approved.txt` in a cc-ci PR — a deliberate, reviewable act. The gate is centralized
in `discovery.py` (one reader) so the unit tests pin it.
- **HC3 — generic-by-default opt-out flag (name/granularity + recipe_meta).** Generic assertions run
**additively** alongside any overlay by default. Opt-out, in increasing specificity (any one skips):
env **`CCCI_SKIP_GENERIC`** (truthy ⇒ skip generic for ALL ops), env
**`CCCI_SKIP_GENERIC_<OP>`** (e.g. `CCCI_SKIP_GENERIC_UPGRADE` ⇒ skip generic for that op only), and
declarative **`recipe_meta.SKIP_GENERIC`** = a list of op names (or `["all"]`) so the opt-out is
per-recipe and visible in git, not a hidden global. Truthy = `1/true/yes/on` (case-insensitive).
**Op-vs-assertion split:** a mutating op (upgrade/backup/restore) is performed **once by the
orchestrator** (the harness owns the op); then the generic assertion file (unless opted out) and the
overlay assertion file both evaluate the **shared post-op state**. Op results that an assertion needs
(pre-upgrade identity, backup snapshot_id) are passed op→assertions via a run-scoped JSON state file
at `$CCCI_OP_STATE_FILE` (read by `harness.generic.op_state()`); never logged. Overlays that need to
**seed pre-op state** (data-continuity markers, the backup→restore mutation) ship an optional
`tests/<recipe>/ops.py` with `pre_install/pre_upgrade/pre_backup/pre_restore(domain, meta)` callables
the orchestrator runs **before** the op (repo-local `ops.py` is allowlist-gated like other repo-local
code). Overlay `test_<op>.py` files are now **assertion-only** (they no longer call `generic.do_*`).
- **HC1 — DG4.1 deploy-count vs the in-place chaos upgrade.** The upgrade tier now upgrades to the
**PR head** (code under test), not a published tag: deploy the previous published version (base),
**re-checkout the PR head** (recorded as the recipe repo HEAD right after fetch, before any
version-tag checkout), then **`abra app deploy --chaos`** in place = the upgrade. The deploy-count
guard counts **`abra app new` installs only** (`_record_deploy()` fires in `deploy_app()`, NOT in the
chaos redeploy, which calls `abra.deploy` directly) — so a run is still **deploy-count == 1** and the
legitimate in-place chaos upgrade is not flagged. **Moved assertion (adapted):** prev→PR-head may not
bump the coop-cloud version label, so `assert_upgraded` accepts ANY of: version-label change, image
change, or a **chaos label** now present carrying the PR-head commit (a chaos deploy stamps
`coop-cloud.<stack>.chaos`/`.chaos-version`) — the chaos label IS the proof PR-head was deployed.
Non-PR `!testme` (no SRC/REF): "PR head" = the catalogue current checkout, so upgrade is prev→current
— still a genuine move via chaos. (Exact chaos label name verified on the live abra during E2.)