Codifies the full path walked for the 2026-08-03 wordpress enrollment (cc-ci PR #14): survey -> mirror create+sync from coopcloud -> author test suite (health floor + non-vacuous recipe-specific tests incl. sec4.3 create-an-object round-trip, recipe-local setup helper, PARITY.md) -> bridge POLL_REPOS -> used-recipes.md weekly row + upstream/<recipe>.md registry -> full-suite-green verification with the new tests -> bridge deploy via test-before-switch -> merge-on-green + report (PR-visibility policy). Includes the traps hit live: creds injection over stdin for cc-ci-side helpers, fresh- deploy wizard state in HEALTH_OK, repo-dev-shell ruff, swarm serialization, stale bridge secret 401s silently dropping !testme.
8.3 KiB
name: recipe-enroll description: Add a NEW recipe to cc-ci's maintained set, end to end — create + sync the recipe-maintainers mirror from coopcloud upstream, author a real test suite (health floor + non-vacuous recipe-specific tests incl. a create-an-object round-trip), enroll it in the !testme bridge (POLL_REPOS) and the weekly /upgrade-all inventory (used-recipes.md weekly row + upstream registry), then VERIFY the whole thing: full harness suite green with the new tests, bridge deployed (test-before-switch) and healthy. Opens the cc-ci PR for visibility and merges it directly once verification is green (the skill invocation is the authorization); the report lists merged PR links + what changed. Invoke as /recipe-enroll .
recipe-enroll
Enroll a coopcloud recipe as a maintained recipe: mirrored, test-covered, !testme-triggerable,
and picked up by the weekly /upgrade-all. This is the full end-to-end path — worked example:
the 2026-08-03 wordpress enrollment (cc-ci PR #14).
"Maintained" = ALL of:
- Mirror
recipe-maintainers/<recipe>exists,main= coopcloud upstream main, tags synced. tests/<recipe>/enrolled in the cc-ci repo (this is what/upgrade-all+ci-test-reviewenumerate).POLL_REPOSinnix/modules/bridge.nixincludes the mirror (the!testmebridge).cc-ci-plan/used-recipes.mdhas a<recipe> weeklyrow (orchestrator repo —/upgrade-allskips recipes without it or taggedexternal).cc-ci-plan/upstream/<recipe>.mdregistry entry (release-notes URLs + standing notes).
Preconditions
- The recipe exists upstream:
ssh cc-ci 'script -qec "abra recipe fetch <recipe>" /dev/null'succeeds (every recipe must have a coop-cloud correspondent). GITEA_*creds in/srv/cc-ci/.testenv(orchestrator side; the cc-ci host does NOT have this file — inject creds over stdin when running helper scripts there, see step 2).- The shared Swarm is quiescent for step 6 (verification deploys the recipe) — do NOT verify
concurrent with
/upgrade-allor other verify runs; author everything first, verify when clear.
Procedure
1. Survey the recipe (read-only)
On cc-ci, after abra recipe fetch <recipe>, read ~/.abra/recipes/<recipe>/:
compose.yml— services + images (for the upstream registry), healthcheck (start_periodinformsDEPLOY_TIMEOUT), traefik labels..env.sample— is the app self-initializing, or does a fresh deploy sit in a setup wizard (e.g. wordpress withoutPOST_DEPLOY_CMDS core_install)? The tests must handle the state a fresh CI deploy actually lands in.- Auth model — how will a test create an object? (REST + token, session login, XML-RPC, …) Check overlay configs (htaccess/nginx templates) for blocked endpoints before relying on one.
2. Create + sync the mirror
Create recipe-maintainers/<recipe> (Gitea API: POST /orgs/recipe-maintainers/repos,
{"name":…,"private":true,"default_branch":"main","auto_init":false}), then force-sync from
coopcloud with the existing helper run on cc-ci with creds injected via stdin (the host has
no .testenv):
set -a; . /srv/cc-ci/.testenv; set +a
{ printf 'export GITEA_USERNAME=%q GITEA_PASSWORD=%q GITEA_URL=%q\n' "$GITEA_USERNAME" "$GITEA_PASSWORD" "$GITEA_URL";
cat /srv/cc-ci-orch/.claude/skills/recipe-upgrade/open-recipe-pr.sh; } \
| ssh cc-ci 'bash -s -- <recipe> --reconcile-only'
Expect: repo created (or exists), main force-synced to upstream, published tags pushed.
3. Author the test suite (dedicated cc-ci clone + branch)
git clone ssh://git@git.autonomic.zone:2222/recipe-maintainers/cc-ci.git /home/loops/work/cc-ci-tests-<recipe>
cd … && git checkout -b test/<recipe>-enroll-$(date -u +%Y%m%d)
tests/<recipe>/ contents (template: tests/uptime-kuma/, tests/wordpress/):
recipe_meta.py—HEALTH_PATH/HEALTH_OK(accept the fresh-deploy state, e.g. a 302 to a setup wizard),DEPLOY_TIMEOUT(recipe healthcheckstart_period+ DB init + first-boot copy, be generous),HTTP_TIMEOUT,WARM_CANONICAL = True(canon §2.B — all recipes enroll as data-warm canonicals, operator 2026-06-17).custom/— the health floor + ≥2 recipe-specific, non-vacuous tests, one of which is the §4.3 create-an-object + read-it-back (write through the app's real API, read back via a different path where possible — e.g. wordpress: XML-RPC write → REST read → permalink HTML). If the app needs setup/auth, put it in a recipe-local_<recipe>.pyhelper (idempotentensure_*so test ordering doesn't matter; run-scoped class-B credentials — the app is destroyed at teardown). Design assertions to name the broken layer (e.g. assert both the rewrite-dependent and rewrite-independent API routes separately).PARITY.md— table of tests × what's verified × why non-vacuous; note there's no recipe-maintainer parity corpus if so.- Lint with the repo dev-shell ruff:
nix develop -c ruff check tests/<recipe>/ && nix develop -c ruff format tests/<recipe>/(pre-existing drift in other files is not yours).
4. Bridge enrollment (same branch)
nix/modules/bridge.nix: append ,recipe-maintainers/<recipe> to the POLL_REPOS= CSV.
5. Inventory + registry (orchestrator repo, direct to main)
cc-ci-plan/used-recipes.md: add<recipe> weeklyrow (alphabetical).cc-ci-plan/upstream/<recipe>.md: images table (source repo + releases/changelog links) + standing notes (setup-wizard behavior, auth caveats, known upgrade traps).- Commit + push (doc commits go direct to main in this repo).
6. Verify end-to-end — full suite GREEN with the new tests (swarm-serialized)
Open the cc-ci PR first (visibility): TITLE=… BODY_FILE=… bash /srv/cc-ci-orch/.claude/skills/ci-test-review/open-cc-ci-pr.sh. The body: what's enrolled,
the test design rationale, the verify + deploy plan.
Then, when the swarm is clear:
ssh cc-ci 'rm -rf /root/cc-ci-test-verify && git clone --branch test/<recipe>-enroll-<date> \
ssh://git@git.autonomic.zone:2222/recipe-maintainers/cc-ci.git /root/cc-ci-test-verify && \
cd /root/cc-ci-test-verify && git submodule update --init secrets 2>/dev/null || true'
RECIPE=<recipe> REMOTE_ROOT=/root/cc-ci-test-verify \
bash /srv/cc-ci-orch/.claude/skills/ci-test-review/verify-pr.sh # no REF — recipe main
Required: cold full-suite green (install/upgrade/backup/restore/custom). Iterate the tests
(bounded, ≤3 attempts) if red — fix the TESTS to match real app behavior, never weaken. Clean up
/root/cc-ci-test-verify after.
7. Deploy the bridge change to the cc-ci host (test-before-switch)
The POLL_REPOS change only takes effect via a host rebuild. Per /cc-ci-server-update steps
5a-e: stage the branch to /root/cc-ci-deploy (+ secrets copy), nixos-rebuild build, detached
nixos-rebuild test, verify reachable + healthy, then switch. Confirm the bridge is polling
the new repo: ssh cc-ci 'docker service inspect ccci-bridge_app --format "{{json .Spec.TaskTemplate.ContainerSpec.Env}}"' | grep <recipe> and the bridge task is 1/1 with no
auth errors in its logs (a stale Gitea secret 401s silently — see the 2026-08-03 finding).
8. Merge + report
Merge the cc-ci PR (invocation = authorization; PR is the visible record — comment the
verification evidence first). Report to the operator: merged PR link + change summary, the
verify log path, and the note that the next weekly /upgrade-all picks the recipe up
automatically (it enumerates tests/<recipe>/ dirs × weekly rows).
Guardrails
- Full-suite green is the enrollment gate — an enrolled-but-red recipe poisons every future sweep. Don't merge on partial green.
- Never weaken: the tests assert the app's real current behavior, incl. asserting removed auth paths are rejected where that's the upstream intent.
- Single-writer: dedicated clones/branches; never push
mainof cc-ci; never touch/root/builder-cloneor the loops' clones;/root/cc-ci-test-verifyis yours — remove after. - Serialize on the swarm: authoring is free, verification + bridge deploy wait for
/upgrade-all/other runs to finish. - abra over ssh needs the pseudo-TTY wrap:
ssh cc-ci 'script -qec "abra …" /dev/null'. - PRs for visibility, merged directly once verified; failed enrollment leaves the PR open with an explanatory comment and the report says exactly what's missing.