Files
cc-ci-orchestrator/cc-ci-plan/plan-phase2w-warm-canonical-quick.md
T
notplantsandClaude Opus 4.8 a2728eec2d plan: Phase 2w — warm canonical deployments + --quick CI mode (interjected into Phase 2)
Operator-directed: pause Phase 2, build the warm-data + --quick system, then resume Phase 2.
- live-warm keycloak (SSO dep, realm-per-run), data-warm canonicals (undeploy keeps volume),
  cold = authoritative default. --quick reattaches the canonical, upgrades to PR head, asserts,
  and rolls back to the last-known-good snapshot on failure (never loses working data).
- known-good = raw volume copy taken while undeployed (consistent), one per app, advanced ONLY
  by green cold runs; a nightly full-cold sweep refreshes canonicals + is a daily regression run.
- launch.sh: insert 2w at the current index (Phase 2 -> resumes after 2w DONE); seq is now
  1c 1b 1d 1e 2w 2 2b 3 4.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-05-28 23:04:33 +01:00

10 KiB
Raw Blame History

cc-ci Phase 2w — Warm canonical deployments + --quick CI mode (Autonomous Build Plan)

Status: ACTIVE — interjected into Phase 2 by operator decision (2026-05-28). Phase 2 (plan-phase2-recipe-tests.md) is PAUSED at its current progress (its STATUS-2/BACKLOG-2 state is preserved); the loops do this phase now, then Phase 2 resumes automatically where it left off. Transition: auto — on ## DONE in machine-docs/STATUS-2w.md the watchdog returns to Phase 2. Builds on: the Phase-1d/1e harness (generic suite, deploy-once, override overlays, HC1 upgrade to PR-head, the sso-dep pattern plan-sso-dep-testing.md) and the now-wired Docker Hub auth. Owner agents: Builder + Adversary loops (plan.md §6/§7); Adversary cold-verifies. This file: /srv/cc-ci/cc-ci-plan/plan-phase2w-warm-canonical-quick.md Phase order now: 1c → 1b → 1d → 1e → 2(paused) → 2w → 2(resume) → 2b → 3 → 4.


0. Why this phase

Cold-start CI (fresh abra app new → deploy → DB-init/first-boot → … → teardown) is slow, and it re-pays that cost on every run and for every SSO dependency. This phase adds a warm-data layer: keep each app's data volume around between runs (Co-op Cloud's undeploy frees RAM but keeps volumes), so a fast --quick run can reattach it, upgrade to the PR code, and assert — without the cold-provisioning cost. A persistent keycloak serves SSO-dependent recipes without a fresh co-deploy each run. A last-known-good snapshot per app means a bad PR tested under --quick can never destroy the working state+data — we roll back.

Terminology (use these terms throughout code/docs/decisions):

  • live-warm — actually deployed and running (e.g. keycloak): instant to use, costs RAM.
  • data-warmundeployed (RAM freed) but its data volume is retained, so a later abra app deploy reattaches it and boots warm (skips fresh DB-init/first-boot), costs only disk.
  • cold — no retained data: fresh abra app new + new volume + full lifecycle + teardown that deletes the volume. The authoritative default.

Design principles settled with the operator (do not relitigate):

  • Keep keycloak live-warm; keep everything else data-warm. Only keycloak (shared dep) + the one app under test run at a time. RAM stops being the limiter; disk is the budget (monitor; bump only if needed — test fixtures are small).
  • Default !testme = full cold (authoritative; its upgrade tier already exercises PR-upgrade per 1e). --quick is an opt-in flag, a lower-confidence fast lane.
  • The canonical known-good advances ONLY via cold runs (esp. the nightly sweep). --quick NEVER promotes the canonical — it consumes it read-mostly and rolls back on failure.
  • Snapshots: raw volume copy taken while UNDEPLOYED (fast + consistent because nothing is writing). One last-known-good per app.
  • Warm volumes + snapshots are cache, not source — not in the git/D8 closure; re-seeded by cold runs, not restored on a VM rebuild.

1. Definition of Done (Phase 2w exit condition)

Terminates when every item holds and the Adversary has independently cold-verified (logged in machine-docs/REVIEW-2w.md):

  • WC1 — Live-warm keycloak (SSO dep). A persistent (live-warm) keycloak runs at a stable domain. SSO-dependent recipes (per plan-sso-dep-testing.md) point their setup_custom_tests at the warm keycloak and create a per-run namespaced realm+client, then delete that realm after the run (cleanup), instead of co-deploying a fresh keycloak. Proven: a dependent recipe's SSO custom tests pass against the warm keycloak; concurrent dependents don't collide (distinct realms); leftover realms are reaped.
  • WC2 — Data-warm canonical model. A canonical per warmed recipe at a stable domain (distinct from cold per-run <recipe>-<6hex> domains), kept data-warm (undeployed-when-idle, volume retained). A small declarative registry/reconciler tracks which recipes are canonical and at which commit their known-good is. Re-warmable from scratch (cache).
  • WC3 — Known-good snapshots. For each canonical app, a raw copy of its data volume(s) taken while undeployed, stored under a stable path (e.g. /var/lib/ci-warm/<recipe>/), tagged with the commit it passed on. One last-known-good retained per app (prior is replaced atomically on update). Restore is proven to bring the app back healthy with its data.
  • WC4 — --quick mode. runner/run_recipe_ci.py gains a --quick path (flag/env): reattach the canonical warm volume (abra app deploy of the canonical) → upgrade to PR head (chaos redeploy) → run generic UPGRADE + serving + custom assertions (generic-first invariant holds) → on PASS: abra app undeploy (keep volume), do NOT alter the known-good; on FAIL: restore the last-known-good snapshot, then undeploy. --quick never promotes the canonical.
  • WC5 — Canonical advancement via cold only. A green full-cold run on latest re-snapshots + re-tags the canonical known-good (promote-on-green instead of deleting at teardown). A cold run is the ONLY thing that advances a canonical. Seeding: the first green cold run on latest makes an app canonical.
  • WC6 — Nightly full-cold sweep. A scheduled job runs the full cold suite across enrolled recipes nightly — refreshing every canonical's known-good (WC5) AND serving as a daily authoritative regression run. Mechanism settled in DECISIONS (systemd timer on cc-ci / Drone cron / bridge), declarative + reproducible. Bounded by MAX_TESTS (serial is fine — nightly).
  • WC7 — Trigger + authority + labeling. Default !testme = full cold (unchanged). --quick is opt-in (!testme --quick, or a build param) and never gates merge. Run results carry the mode (cold vs quick) so a --quick pass is distinctly labeled lower-confidence (feeds Phase 3). Quick requires an existing canonical; if none, it cleanly falls back to (or reports "no canonical — run cold first").
  • WC8 — Resource safety + isolation. Warm-base runs serialize per app (MAX_TESTS honored); warm keycloak shared safely via per-run realms; disk monitored (warm volumes + one snapshot each) with a documented budget + prune of stale/orphaned warm data; cold-run teardown stays sacred (deletes its own per-run volumes); warm data is excluded from the D8 reproducibility closure (documented as cache).
  • WC9 — Documented + cold-verified, incl. the rollback proof. docs/ explains warm/quick; the Adversary cold-verifies, including deliberately failing a PR under --quick and confirming the canonical's last-known-good is restored intact (data preserved), and that a --quick pass did not move the known-good. No softened tests.

When WC1WC9 hold and are confirmed, write ## DONE to machine-docs/STATUS-2w.md → the watchdog auto-returns to Phase 2 (resume recipe authoring).


2. The --quick flow (reference)

PRECOND: a canonical for <recipe> exists (seeded by a prior green cold run); else fall back/report.
  1. abra app deploy <canonical-domain>      # reattach warm volume -> fast warm boot at known-good commit
  2. wait_healthy
  3. (deps) point at the warm keycloak; create a per-run realm+client (namespaced)
  4. UPGRADE to PR head (abra app deploy --chaos to the PR checkout)   # the op, once
  5. assert: generic upgrade (reconverge + moved + serving) + recipe overlay + custom (requires_deps)
  6a. PASS -> abra app undeploy <canonical-domain>      # keep volume; known-good UNCHANGED
  6b. FAIL -> restore last-known-good snapshot to the volume; abra app undeploy   # roll back, data safe
  7. (deps) delete the per-run realm from the warm keycloak

Cold run (default, unchanged) seeds/advances the canonical: on a green cold run on latest, snapshot the (undeployed) volume → replace the last-known-good + tag the commit, and keep the volume as the new canonical instead of deleting it.

3. Milestones (bounded)

  • W0 — Warm keycloak (WC1). Highest ROI; unblocks faster SSO recipe tests for the resumed Phase 2.
  • W1 — Canonical registry + snapshot/restore (WC2, WC3). Stable-domain warm apps; raw-while- stopped snapshot + restore; prove restore round-trips data.
  • W2 — --quick mode (WC4, WC7). Orchestrator path + labeling + fallback.
  • W3 — Cold-advances-canonical + nightly sweep (WC5, WC6). Promote-on-green-cold; scheduled job.
  • W4 — Resource/isolation hardening + docs + cold verify incl. rollback proof (WC8, WC9). Then ## DONE.

4. Guardrails

  • --quick never advances the canonical; only cold does. Anchors the baseline to verified states.
  • Never lose the known-good — snapshot before mutate (or rely on the standing known-good); restore on any quick failure. The rollback proof (WC9) is mandatory.
  • Default stays cold; quick is opt-in + clearly lower-confidence. Don't let a quick pass read as full confidence.
  • Snapshot only while undeployed (consistency). One last-known-good per app (disk).
  • Cold teardown stays sacred (deletes per-run volumes); warm volumes are a managed cache, never confused with per-run state; warm data excluded from D8.
  • Never weaken a test (cardinal rule). Generic-first invariant holds in --quick too.
  • Bounded — build the mechanism + prove on keycloak + a couple of recipes; do NOT re-warm all recipes here (the nightly sweep populates canonicals over time).

5. Open decisions (log in machine-docs/DECISIONS.md)

  • Canonical stable-domain scheme (distinct from cold per-run domains) + how the registry/reconciler is declared.
  • Snapshot storage + format (raw tar vs reflink/CoW copy) under /var/lib/ci-warm/; atomic replace.
  • Nightly sweep mechanism (systemd timer / Drone cron / bridge) + ordering + disk-prune policy.
  • --quick trigger surface (!testme --quick comment vs Drone build param) + the "no canonical yet" fallback (run cold vs report-and-skip).
  • Disk budget: measure warm volume + snapshot sizes across recipes; decide if a 30→larger bump is needed or the warm set stays bounded.