# 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, DG1–DG8 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//ops.py` (`pre_(domain, meta)`); overlay `test_.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. ## 2026-05-28 — E0 / HC2 repo-local trust gate (DONE, CLAIMED) - Implemented the approval allowlist (`tests/repo-local-approved.txt`, default empty ⇒ default-deny) + centralized gate in `runner/harness/discovery.py`: `approved_recipes()`/`repo_local_approved()`/ `_gated()`. Split overlay resolution into `resolve_overlay_op` (repo-local>cc-ci, gated) + `generic_op` (the floor) for HC3; kept back-compat `resolve_op` (override). `custom_tests`/`install_steps`/new `pre_op_hook` all route repo-local through `_gated`. Allowlist path overridable via `CCCI_REPO_LOCAL_APPROVED_FILE`. - Rewrote `tests/unit/test_discovery.py` for the gate (approved-vs-not for overlay/custom/hook/pre-op + the generic floor + default-empty-allowlist invariant). - Verified on cc-ci (tar-piped working tree → /root/cc-ci; cc-ci has no rsync): `cc-ci-run -m pytest tests/unit -q` → **8 passed in 0.06s** And the cc-ci-authored hook is unaffected (DG5): discovery.install_steps("custom-html-tiny", None) → ('cc-ci', '.../tests/custom-html-tiny/install_steps.sh') - Committed d38a695, pushed. Gate E0/HC2 CLAIMED for Adversary. Next: E1 (HC3) — orchestrator op/assertion split + additive generic + opt-out + overlay migration. ## 2026-05-28 — E1 / HC3 additive generic + op/assertion split (implemented + e2e verified) - **Harness core:** `lifecycle.deployed_identity` now returns `{version,image,chaos}` (chaos label captured, ready for HC1). `generic.py` split: op primitives `perform_upgrade/perform_backup/ perform_restore` (orchestrator-only, no asserts) + assertions `assert_upgraded` (serving + MOVED via version/image/chaos), `assert_backup_artifact`, `assert_restore_healthy`, all reading the run-scoped `op_state()` (`$CCCI_OP_STATE_FILE`). - **Orchestrator** (`run_recipe_ci.py`): new `run_lifecycle_tier` = pre-op seed hook (`ops.py pre_`, imported in-process w/ recipe dir on sys.path) → perform the op ONCE → run generic assertion (unless `_skip_generic`) + overlay assertion, both against the shared post-op deployment. Opt-out: `CCCI_SKIP_GENERIC` / `CCCI_SKIP_GENERIC_` / `recipe_meta.SKIP_GENERIC`. `_scrub` factored so op-failure messages are redacted too. Op primitives never call `deploy_app` ⇒ deploy-count stays 1. - **Tiers/overlays migrated to assertion-only:** generic `_generic/test_{upgrade,backup,restore}.py`; all 6 recipes' `test_{upgrade,backup,restore}.py`. Pre-op seeding (data-continuity markers + the backup→restore mutation) moved to per-recipe `ops.py` (`pre_upgrade/pre_backup/pre_restore`). install overlays unchanged (no op). No assertion weakened — every data-survival/return check kept. - **Verified on cc-ci:** - `cc-ci-run -m pytest tests/unit -q` → **8 passed**; `nix develop .#lint` → **lint: PASS** (ruff format + check clean). - Full e2e `RECIPE=custom-html STAGES=install,upgrade,backup,restore,custom` → every tier ran BOTH generic AND overlay (additive): install(generic test_serving + overlay test_serving_and_content), upgrade(pre_upgrade seed → generic test_upgrade_reconverges + overlay test_upgrade_preserves_data), backup(pre_backup → generic test_backup_artifact + overlay test_backup_captures_state), restore(pre_restore → generic test_restore_healthy + overlay test_restore_returns_state). **RUN SUMMARY: deploy-count=1, install/upgrade/backup/restore=pass, custom=skip; no leftover custom-html stack (clean teardown).** Log: /root/ccci-1e-customhtml.log on cc-ci. - Opt-out run (`CCCI_SKIP_GENERIC=1`) in flight to show generic skipped + overlay still runs. Next: confirm opt-out result, claim E1/HC3 gate, then E2 (HC1 chaos-to-PR-head). ## 2026-05-28 — E1 opt-out verified; gate CLAIMED - Opt-out e2e `RECIPE=custom-html STAGES=install,upgrade,backup,restore CCCI_SKIP_GENERIC=1`: every tier logged `generic=skip, overlay=cc-ci`; **0** `_generic/test_*` files ran; only the 4 cc-ci overlays ran; **deploy-count=1**; install/upgrade/backup/restore=pass; clean teardown (no leftover custom-html stack). Log: /root/ccci-1e-optout.log. - HC3 proven both ways: default = generic+overlay additive on one deployment (op once); opt-out = generic floor skipped, overlay still runs. Gate E1/HC3 CLAIMED for Adversary.