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

@ -0,0 +1,19 @@
# BACKLOG — Phase 1e (generic-harness corrections)
Phase-namespaced backlog. Builder edits `## Build backlog`; Adversary edits `## Adversary findings`.
## Build backlog
- [ ] **E0 / HC2** — repo-local approval allowlist (`tests/repo-local-approved.txt`, default-deny);
gate `discovery.resolve_op`/`custom_tests`/`install_steps` behind `repo_local_approved(recipe)`;
update unit tests (`tests/unit/test_discovery.py`) for approved vs non-approved.
- [ ] **E1 / HC3** — generic-by-default (additive); op/assertion split. Orchestrator performs each
mutating op once; runs generic test_<op>.py (unless opt-out) + overlay test_<op>.py. Opt-out:
`CCCI_SKIP_GENERIC` / `CCCI_SKIP_GENERIC_<OP>` / `recipe_meta.SKIP_GENERIC`. Pre-op seed via
optional `tests/<recipe>/ops.py`. Migrate generic + overlays to assertion-only. Keep count==1.
- [ ] **E2 / HC1** — upgrade to PR head via `abra app deploy --chaos`: deploy prev, re-checkout PR
head, chaos redeploy in place; adapt moved-assertion (chaos label proof); reconcile deploy-count.
- [ ] **E3 / HC4** — docs (docs/testing.md, enroll-recipe.md) + DECISIONS; claim gates; await Adversary
cold-verify of HC1HC4; flip STATUS-1e → ## DONE on full PASS.
## Adversary findings
(none yet)

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.)

View File

@ -0,0 +1,23 @@
# JOURNAL — Phase 1e (generic-harness corrections)
Append-only Builder log: what I did + verifying command/output + next.
## 2026-05-28 — Phase 1e bootstrap + orientation
- Read the phase plan (`plan-phase1e-harness-corrections.md`) + plan.md §6.1/§7/§9. Phase 1d is DONE
(STATUS-1d ## DONE, DG1DG8 Adversary PASS). Studied the harness: `runner/run_recipe_ci.py`
(deploy-once orchestrator), `runner/harness/{discovery,generic,lifecycle,abra}.py`, `tests/conftest.py`,
`tests/_generic/*`, the overlays (custom-html/keycloak/cryptpad/n8n/matrix-synapse), and
`tests/unit/test_discovery.py`.
- Access re-verified: `ssh cc-ci 'hostname && whoami'``nixos` / `root`.
- Settled the three open decisions (HC1 deploy-count, HC2 allowlist, HC3 opt-out) in DECISIONS.md.
- Created STATUS-1e / BACKLOG-1e / JOURNAL-1e. Order of work: E0 (HC2) → E1 (HC3) → E2 (HC1) → E3.
- Key design notes:
- HC3 op/assertion split: orchestrator performs each mutating op once; generic + overlay both run as
assertions after. Op results (pre-upgrade identity, snapshot_id) passed via run-scoped
`$CCCI_OP_STATE_FILE`. Overlays that seed pre-op state move that into an optional
`tests/<recipe>/ops.py` (`pre_<op>(domain, meta)`); overlay `test_<op>.py` become assertion-only.
- HC1: re-checkout PR head (recorded as recipe HEAD right after fetch) then `abra app deploy --chaos`;
moved-assertion accepts the chaos label as proof PR-head deployed; deploy-count counts only
`deploy_app` (app new), not the in-place chaos redeploy.
Next: E0 — implement the HC2 allowlist + discovery gate + unit tests.

38
machine-docs/STATUS-1e.md Normal file
View File

@ -0,0 +1,38 @@
# STATUS — Phase 1e (generic-harness corrections HC1HC4)
**Phase plan (SSOT):** `/srv/cc-ci/cc-ci-plan/plan-phase1e-harness-corrections.md`
**Loop state for THIS phase:** STATUS-1e / BACKLOG-1e / REVIEW-1e / JOURNAL-1e (DECISIONS.md shared).
Phase-1/1b/1c/1d STATUS/BACKLOG/REVIEW files are HISTORY (1d DONE) — not this phase's state.
## Phase
Phase 1e corrects the Phase-1d shared generic-test harness, before Phase 2 authors overlays on top.
Three corrections, each Adversary cold-verified, no test weakened:
- **HC1** — upgrade tier upgrades to the **PR head** (code under test) via `abra app deploy --chaos`,
not a published tag.
- **HC2** — repo-local (PR-authored) `test_*.py`/`install_steps.sh` run **only for recipes on an
explicit cc-ci approval allowlist** (default-deny); else cc-ci+generic only.
- **HC3** — the **generic runs by default (additive)** alongside any overlay; skipping it is explicit
(env/recipe_meta opt-out). Op runs once (harness-owned); generic + overlay assertions both evaluate
post-op state.
- **HC4** — Adversary cold re-verifies no regression (D1D10/DG1DG8) + the three new behaviors.
## Definition of Done (Phase 1e) — HC1HC4, each Adversary cold-verified in REVIEW-1e
- [ ] **HC1** — PR-head upgrade proven to deploy PR-head; deploy-count guard reconciled (==1).
- [ ] **HC2** — repo-local ignored for a non-approved recipe, run for an approved one.
- [ ] **HC3** — generic runs alongside an overlay by default; skipped only with the opt-out set.
- [ ] **HC4** — no regression cold-verified; deploy-once + teardown still sacred.
## Milestones (plan §3)
- **E0** — HC2 trust gate (allowlist, default-deny). *Accept: repo-local ignored unless approved.*
- **E1** — HC3 additive + op/assertion split. *Accept: overlay+generic both run; opt-out skips; count=1.*
- **E2** — HC1 upgrade-to-PR-head. *Accept: upgrade demonstrably deploys PR-head.*
- **E3** — HC4 cold re-verification + docs → DONE.
## In flight
E0 (HC2) — implementing the repo-local approval allowlist + discovery gate + unit tests.
## Gate
(none claimed yet)
## Blocked
(none) — bootstrap access re-verified @2026-05-28: `ssh cc-ci` ok (root, NixOS).