Commit Graph

315 Commits

Author SHA1 Message Date
e73e4393ed fix(2w): docker autoPrune drop --volumes (was failing daily + would wipe warm vols) [WC8]
The autoPrune flags passed '--volumes' WITH '--filter until=24h', which docker
rejects ('until filter not supported with --volumes') — so docker-prune.service
FAILED every day (system 'degraded') and never reclaimed anything (a cause of the
disk creeping to 96%). Worse, '--volumes' prunes volumes with no running
container — which would DELETE Phase-2w DATA-WARM canonical volumes (undeployed by
design). Removed '--volumes': now prunes images/containers/networks/build-cache
older than 24h only; warm volumes survive and are pruned deliberately by the warm
reconcilers (WC8).

Verified: nixos-rebuild switch -> docker-prune.service runs clean, system
'running' (0 failed units), warm keycloak still 200.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-05-29 01:26:24 +01:00
819c1bc0fd status+journal(2w): W0.9 WC1.1 live proofs PASS (healthy upgrade + marquee rollback); reconciler-side WC1/WC1.1/WC1.2 proven 2026-05-29 01:21:59 +01:00
32f00717ac fix(2w): W0.9 WC1.1 hardening (proven live: healthy upgrade + marquee rollback)
Bugs found by the live proof, fixed:
- warmsnap: snapshot now swaps a <recipe>/snapshot/ SUBDIR, not the whole
  <recipe>/ dir — so the reconciler's sibling last_good file survives a
  snapshot swap (was being clobbered).
- warm_reconcile: deploy_version captures abra's stdout (it writes FATA to
  stdout) in the error; add wait_undeployed() after every undeploy so
  snapshot/restore/redeploy don't race a half-removed swarm stack; the upgrade
  deploy is wrapped so a deploy FAILURE (not just unhealthy) also triggers
  rollback. (57 unit pass.)

LIVE PROOF on warm keycloak (annotated fake tags via CCCI_SKIP_FETCH):
(a) healthy upgrade 10.7.1->10.7.9: snapshot+deploy+health-pass, last_good
    committed=10.7.9, marker realm preserved.
(b) MARQUEE rollback: broken latest 10.7.10 (lint-fail) -> rollback to 10.7.9,
    HEALTHY, marker realm INTACT (data preserved through broken-upgrade+restore),
    last_good NOT advanced, rollback alert written (attempted=10.7.10,
    last_good=10.7.9, recovered=True). keycloak recovered to canonical
    10.7.1+26.6.2 healthy.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-05-29 01:21:05 +01:00
07ea951f31 fix(2w): WC1.1 reconcile rolls back on deploy FAILURE too (not just unhealthy)
A broken 'latest' can fail abra's converge (deploy_version raises) rather than
deploy-then-be-unhealthy; wrap the upgrade deploy so BOTH paths trigger the
snapshot-restore rollback instead of crashing the reconcile unit.
2026-05-29 01:01:28 +01:00
0812132452 review(2w): standing WC8 probe — lasu-0a6fb2 fully torn down (no app/svc/vol/secret), disk 63% 2026-05-29 00:55:49 +01:00
4808d0354a status(2w): W0.6 reconciler delivered + WC1.2 holds proven; next W0.9 WC1.1 live proofs 2026-05-29 00:43:10 +01:00
a044abb298 feat(2w): W0.6 unpinned warm reconciler + WC1.2 safety gate + WC1.1 scaffold
runner/warm_reconcile.py (python, packaged into nix store, replaces bash
reconcile): UNPIN keycloak (deploy latest published version TAG; recipe fetched
at runtime -> D8 closure byte-identical). WC1.2 pre-deploy safety gate (runs
FIRST): major recipe/app-version bump OR releaseNotes manual-migration marker
-> hold-on-current + alert sentinel (no deploy churn). WC1.1 health-gated
upgrade-with-rollback: record last-good -> [keycloak: undeploy->warmsnap.snapshot
->deploy latest] -> health-gate -> commit-or-(restore+redeploy-prior+alert).
Alerts = /var/lib/ci-warm/alerts/*.json (Builder loop relays). current version
read from abra TYPE=<recipe>:<version>. CCCI_SKIP_FETCH test hook.
+8 unit tests for the version gate (56 unit pass).

Proven on cc-ci: nixos-rebuild switch -> warm-keycloak.service runs the python
reconciler -> noop-healthy (system 0-failed, /realms/master=200). WC1.2 holds
proven live: MAJOR bump -> held-major (keycloak untouched); minor+manual-
migration notes -> held-manual-migration (alert carries notes); no deploy churn.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-05-29 00:42:02 +01:00
aff50aac0a journal(2w): W0.5 proven + WC8 disk reclaim (96%->62%); checkpoint before W0.6 2026-05-29 00:29:42 +01:00
67240dca92 decisions+status(2w): W0.5 done (WC3 snapshot proven); W0.6 reconciler version model (deploy-by-tag, recipe-semver pre-+, python entrypoint in store) 2026-05-29 00:15:38 +01:00
4cc1e15a53 feat(2w): W0.5 WC3 snapshot/restore helper (warmsnap.py)
runner/harness/warmsnap.py: raw per-volume tar of an app's stack volumes while
UNDEPLOYED, under /var/lib/ci-warm/<recipe>/ (meta.json + volumes/<vol>.tar);
one last-good, atomic dir swap; restore clears+untars each volume back. Asserts
undeployed (consistency). Reused by WC1.1 (pre-upgrade keycloak snapshot) + WC5.
+5 unit tests (48 unit pass).

LIVE round-trip PROVEN on warm keycloak: create marker realm -> undeploy ->
snapshot (mariadb+providers vols) -> deploy -> delete marker (mutate DB) ->
undeploy -> restore -> deploy -> marker realm BACK; keycloak healthy. WC3 core.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-05-29 00:12:46 +01:00
ceacd0e6de backlog+decisions(2w): re-sequence W0 (WC3 helper first); unpin/snapshot/alert decisions 2026-05-29 00:05:13 +01:00
740d7bac4c status(2w): W0 core mechanism proven + reconciler up; absorb design update (unpin+WC1.1+WC1.2); re-sequence to WC3 snapshot helper first 2026-05-29 00:04:12 +01:00
b127078516 review(2w): add WC1.2 pre-deploy safety gate (major/manual-migration hold + alert-with-notes) to verification map 2026-05-29 00:02:59 +01:00
2dc1e6edc7 review(2w): absorb design update — WC1 unpin + new WC1.1 health-gated rollback proof + WC6 reorder into verification map 2026-05-29 00:00:09 +01:00
88c11142de fix(2w): W0.3 warm-keycloak reconciler — newline bite + skip-if-healthy
- set_env: ensure trailing newline before append (keycloak .env.sample ends
  with a newline-less #COMPOSE_FILE comment, so a bare append glued DOMAIN onto
  it -> DOMAIN unset -> KC_HOSTNAME=https:// -> crash-loop). Same bite fixed in
  backupbot.nix.
- converge skips the (forced) redeploy when keycloak already serves 200, so an
  activation/boot is a true no-op (no JVM-restart blip) and only redeploys when
  down/crash-looping. Health-wait extended to 15min.

Verified on cc-ci: nixos-rebuild switch -> warm-keycloak.service active,
'no-op converge', system running (0 failed), /realms/master=200.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-05-28 23:52:01 +01:00
c8e9ddb681 feat(2w): W0.3 declarative warm-keycloak reconciler (WC1)
nix/modules/warm-keycloak.nix: idempotent systemd oneshot (like deploy-proxy)
that converges a live-warm shared keycloak at warm-keycloak.ci.commoninternet.net
pinned to  10.7.1+26.6.2, secrets generated only-if-missing (never
rotate a live provider), waits /realms/master=200. Re-warmable from scratch
(D8/WC8). Wired into hosts/cc-ci/configuration.nix.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-05-28 23:28:44 +01:00
1b8d26b504 feat(2w): W0.2 live-warm keycloak dep mode in orchestrator (WC1)
- runner/harness/warm.py: stable-domain scheme (warm-<recipe>), is_warm_up
  probe, live_app_hexes scan, per-run realm_for naming, reap_orphan_realms.
- run_recipe_ci.py: split declared deps into live-warm (shared provider +
  per-run realm, no deploy, realm deleted at teardown) vs cold (co-deploy).
  Warm path used only when provider is up; cold fallback otherwise. Reap
  orphan realms at run start (concurrency-safe). deploy-count excludes warm
  deps. Realm naming now per-run namespaced (<parent>-<6hex>).
- dependent tests assert the namespaced realm pattern (stronger than ==parent).

Live proof on warm keycloak: realm create -> password-grant JWT -> discovery
issuer -> delete(idempotent) -> reap(keeps live hex, deletes orphan): PASS.
43 unit pass.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-05-28 23:26:02 +01:00
74bf8c1723 feat(2w): W0.1 keycloak realm lifecycle primitives (WC1)
sso.py: list_realms, delete_keycloak_realm (idempotent, refuses master),
realms_to_reap (pure, concurrency-safe predicate), reap_orphaned_realms.
The per-run realm is the isolation unit on a shared live-warm keycloak;
orphans (crashed runs) reaped by hex not mapping to a live app stack.
+8 unit tests (tests/unit/test_warm_realm.py); 43 unit pass on cc-ci.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-05-28 23:16:48 +01:00
5dd76d7c8c chore(2w): bootstrap Phase 2w loop state + cleanup orphaned cold apps
- Seed STATUS-2w / BACKLOG-2w / JOURNAL-2w (WC1-WC9 DoD, W0-W4 milestones).
- Tore down leftover Phase-2 cold apps (lasu-0a6fb2/keyc-07d81e/lasu-dbg);
  disk 91%->86%.
- DECISIONS: warm-domain scheme, per-run realm isolation, warm keycloak as
  declarative infra, cold fallback.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-05-28 23:14:41 +01:00
66e065dff5 feat(2): lasuite-drive setup creates MinIO bucket via createbuckets one-shot
In-flight Q3.2 iteration (NOT yet live-verified — needs a lasuite-drive deploy
once the warm keycloak from Phase 2w is available). Phase 2 paused here per
operator interjection of Phase 2w; state preserved.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-05-28 23:08:15 +01:00
534cd7066c review(2w): Adversary online — phase start, cold access verified, awaiting WC gate claims 2026-05-28 23:07:04 +01:00
6557197858 feat(2): Q3.2 lasuite-drive SSO iteration — keycloak dep + OIDC test + MinIO storage round-trip
- recipe_meta: DEPS=[keycloak] enabled (base proven cold-green).
- setup_custom_tests.sh: wire OIDC env (explicit keycloak realm endpoints) + insert oidc_rpcs
  secret at bumped version + clear FranceConnect eidas1 acr + in-place redeploy (adapted from
  the proven lasuite-docs hook).
- functional/test_oidc_with_keycloak.py: SSO discovery + password grant + JWT claims vs dep
  keycloak realm 'lasuite-drive' (@requires_deps; F2-11 fails run on skip).
- functional/test_minio_storage.py: §4.3 specific — drive-media-storage bucket present + real
  upload->list->download round-trip via mc inside the minio container.
- PARITY.md: OIDC + MinIO rows landed; backup data-integrity (ci_marker) already real.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-05-28 22:28:35 +01:00
5f1ce47593 review(2): rate-limit fix VERIFIED + CLOSED — all 3 conditions cold (auth 200-limit, own uncached swarm-service pull, declarative sops persistence); consume inbox
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-05-28 22:17:23 +01:00
15228c2fdb inbox(2): signal Adversary — Docker Hub auth wired, conditions 2+3 proven (uncached n8n swarm pull + declarative sops persistence)
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-05-28 22:13:57 +01:00
7a337f5d69 status(2): Docker Hub rate-limit RESOLVED — declarative sops auth + swarm pulls authenticate (3 conditions); DECISIONS recorded
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-05-28 22:13:25 +01:00
5e14963d51 feat(2): declarative Docker Hub auth — sops dockerhub_auth + config.json template (rate-limit fix)
- secrets submodule -> cdd5e0a (adds sops dockerhub_auth = base64 nptest2:PAT).
- nix/modules/secrets.nix: sops.secrets.dockerhub_auth + sops.templates."docker-config.json"
  renders /root/.docker/config.json (0600 root) so abra/docker pulls authenticate (200/6h
  per-account) instead of the exhausted 100/6h shared-IP anon limit. Survives 1c rebuild.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-05-28 22:05:10 +01:00
46e9d1c43a review(2): rate-limit PARTIAL verify — auth 200-limit + account source CONFIRMED; swarm-pull + declarative-persistence still pending
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-05-28 22:04:03 +01:00
45fb42e19d review(2): rate-limit fix pre-wiring baseline (anon 100/6h @68.14.43.142, remaining=4); verification plan for post-wiring
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-05-28 21:45:57 +01:00
65e4e519ff review(2): F2-11 CLOSED — deploy-free cold proof (35 unit + real conftest skip-report stitched to predicate); consume inbox
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-05-28 21:29:32 +01:00
0d6cd05675 inbox(2): notify Adversary — F2-11 fixed (deploy-free verify) + deploy work paused on Docker Hub rate limit
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-05-28 21:25:57 +01:00
5b34496557 fix(2): F2-11 — SSO-dep deps-not-ready SKIP no longer yields GREEN !testme
When a DEPS-declaring recipe's setup_custom_tests fails, its @requires_deps (SSO/OIDC)
tests skip; a skip-only pytest file exits 0 so the run previously reported overall=0
(GREEN) while the only SSO test never ran (violates P7). Fix preserves generic-tier
failure-isolation but corrects the green SIGNAL:
- conftest.pytest_collection_modifyitems counts skipped requires_deps tests and appends
  to $CCCI_DEPS_SKIP_REPORT.
- run_recipe_ci: sums the count, surfaces it in RUN SUMMARY, and new pure predicate
  sso_dep_unverified(declared, deps_ready, skipped) flips overall=1.
- 7 new unit tests (tests/unit/test_f211_sso_skip.py).

Verified deploy-free (rate-limit-independent): 35/35 unit PASS; cold real-test proof on
lasuite-docs test_oidc_with_keycloak.py -> 1 skipped + skip-report==1 -> orchestrator
would set overall=1. Full e2e deferred until Docker Hub rate limit lifts.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-05-28 21:25:27 +01:00
10d2a13031 chore(2): consume BUILDER-INBOX (Adversary DONE-gate warnings + F2-11 SSO-skip-goes-green)
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-05-28 21:19:35 +01:00
aae31775ae status(2): Gitea outage resolved + git reconciled; Docker Hub rate-limit block stands (registry-creds finding)
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-05-28 21:18:52 +01:00
b941f552a1 review(2): file F2-11 — SSO deps-not-ready SKIP yields GREEN !testme (cold-proven); note git host outage
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-05-28 21:17:05 +01:00
900b427444 review(2): idle checkpoint — cold access OK; consolidated Phase-2 DONE-gate conditions (F2-7, F2-9, ghost §4.3 floor); lasuite-drive Q3.2 base WIP noted
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-05-28 21:17:05 +01:00
4a118eafee journal(2): correct drive note — cannot trim onlyoffice (recipe-as-is); registry creds is the fix
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-05-28 20:56:31 +01:00
1138d77cbb blocked(2): Q3.2 drive base-deploy hits Docker Hub rate limit + Gitea outage
- recipe_meta: bump drive abra TIMEOUT 900->1500, DEPLOY_TIMEOUT 1200->1800 (12-svc
  stack w/ onlyoffice+collabora; cold pulls need a wide window).
- STATUS-2 ## Blocked: two Class-A1 external blocks documented w/ verify commands —
  (1) Docker Hub anon pull rate limit (registry-creds finding per plan §1.5; blocks all
  new deploys), (2) Gitea git.autonomic.zone 404 outage (coordination down; 2 watchdog
  pings unconsumable until recovery). JOURNAL-2: full disk->prune->rate-limit chain.
- Queued locally; push + Adversary-inbox processing deferred to Gitea recovery.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-05-28 20:48:52 +01:00
f59d8e6996 feat(2): Q3.2 lasuite-drive base enrollment + nested-subdomain + replicas:0 harness fixes
- harness: services_converged treats replicas:0 one-shot (minio-createbuckets) as
  converged (cur==want); removes the want==0 rejection that hung deploys. DECISIONS.md.
- recipe_meta.EXTRA_ENV flattens MINIO_DOMAIN/COLLABORA_DOMAIN to single-label wildcard
  siblings (the *.ci.commoninternet.net cert covers one label only). DECISIONS.md.
- lifecycle overlays (install/upgrade/backup/restore) + ops.py postgres ci_marker
  data-integrity (db user/name=drive). Parity health_check functional test. PARITY.md.
- DEPS=[keycloak] + OIDC/WOPI/upload functional tests deferred to the SSO iteration
  (probe-before-assert: prove the ~10-service base deploy converges first).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-05-28 19:54:31 +01:00
9aa045de86 deferred(2): close DEFERRED #5 (lasuite-docs OIDC); open upload_conversion as follow-up 2026-05-28 19:28:23 +01:00
cd25f52eae feat(2): close DEFERRED #5 — lasuite-docs OIDC parity + create-a-doc (§4.3) cold green
Per orchestrator's SSO-dep plan + the refactor in 41ede13, DEFERRED.md entry #5 (lasuite-docs
OIDC parity ports + create-a-doc) closes by execution.

- tests/lasuite-docs/functional/test_oidc_login.py: parity port of recipe-maintainer
  oidc_login.py. Anonymous GET /api/v1.0/users/me/ → 302 to keycloak realm OR 401/403;
  password-grant token → 200 with user.email matching the provisioned test user.
- tests/lasuite-docs/functional/test_create_doc.py: plan §4.3 prescribed create-an-object +
  read-it-back. POST /api/v1.0/documents/ with OIDC Bearer → captured id; GET
  /api/v1.0/documents/<id>/ → asserts id+title round-trip.

Both marked \@pytest.mark.requires_deps; skipped with 'deps-not-ready' if setup_custom_tests
fails (failure isolation per plan-sso-dep-testing.md §4).

Cold-verifiable: ssh cc-ci 'RECIPE=lasuite-docs STAGES=install,custom cc-ci-run runner/run_recipe_ci.py'
  install: 2 PASS; custom: 5 PASS incl. test_oidc_login_via_keycloak +
  test_create_doc_and_read_back; deploy-count=2 (recipe + keycloak dep).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-28 19:26:54 +01:00
41ede13042 feat(2): refactor — SSO-dep plan refinement (deps AFTER generic + setup_custom_tests + failure isolation)
Per operator-2026-05-28 SSO-dep plan (plan-sso-dep-testing.md). Substantial orchestrator
restructuring:

NEW LIFECYCLE ORDER:
  1. Recipe deploy ALONE (no deps).
  2. install / upgrade / backup / restore — recipe-only generic tiers.
  3. setup_custom_tests step (NEW):
     a. Deploy each declared dep + provision realm/client/test-user via harness.sso.
     b. Write $CCCI_DEPS_FILE in dict shape {dep_recipe: {domain, realm, client_id, client_secret,
        admin_user, admin_password, discovery_url, token_url, ...}}.
     c. Run tests/<recipe>/setup_custom_tests.sh hook (jq-readable; wires OIDC env via abra
        secret insert + .env edits + in-place 'abra app deploy --force --chaos').
  4. CUSTOM tier with deps-ready flag; @pytest.mark.requires_deps tests skip with
     'deps-not-ready: <reason>' when setup_custom_tests fails. NON-deps custom tests still run
     normally — FAILURE ISOLATION (a DoD item per plan).
  5. Teardown: recipe first, deps in reverse declaration order.

Harness changes:
- runner/run_recipe_ci.py: deps deploy moves from BEFORE recipe deploy to AFTER restore tier.
  Adds _enrich_deps_with_sso() + _run_setup_custom_tests_hook(). DG4.1 generalised to
  'one abra app new per app' (recipe + each dep); in-place redeploys (\--force) don't count.
- runner/harness/deps.py: write_run_state + load_run_state accept dict OR list shape;
  deps_as_dict() coerces either to a recipe→entry map.
- runner/harness/sso.py: admin_password_inside() public re-export.
- tests/conftest.py: deps_creds fixture (full creds dict); deps_apps fixture flattens to
  recipe→domain string. pytest_collection_modifyitems hook skips
  \@pytest.mark.requires_deps tests when CCCI_DEPS_READY=0.
  pytest_configure registers the marker.

Recipe content:
- tests/lasuite-docs/setup_custom_tests.sh: NEW hook reads $CCCI_DEPS_FILE via jq;
  inserts oidc_rpcs secret at BUMPED version (v1→v2) since abra app new -S generates v1 first
  and Swarm forbids overwriting; updates SECRET_OIDC_RPCS_VERSION in .env; writes 9 OIDC env
  vars (REALM/DISCOVERY/AUTH/TOKEN/USERINFO/LOGOUT/JWKS/CLIENT_ID/SCOPES); ensures trailing
  newline on .env so writes don't concatenate (caught a 'TIMEOUT=900OIDC_REALM=...' bug);
  triggers in-place 'abra app deploy --force --chaos --no-input'.
- tests/lasuite-docs/functional/test_oidc_with_keycloak.py: refactored to consume deps_creds
  fixture (no longer calls setup_keycloak_realm itself — the orchestrator does it in
  setup_custom_tests). Marked \@pytest.mark.requires_deps.

Cold-verifiable on cc-ci (log /root/ccci-refactor-lasuite-r5.log):
  RECIPE=lasuite-docs STAGES=install,custom cc-ci-run runner/run_recipe_ci.py
  install: PASS, custom: 3 PASS incl. test_oidc_password_grant_against_dep_keycloak.
  deploy-count = 2 (expect 2) — DG4.1 generalised holds.
  Smoke regression: RECIPE=custom-html STAGES=install,custom → 5 PASS, deploy-count=1.

Closes DEFERRED.md #5 (lasuite-docs OIDC parity ports via this plan).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-28 19:11:42 +01:00
5832da4fd1 deferred(2): Q4.7 plausible — drafted but 500 on cold-start, defer for operator-iterate
tests/plausible/recipe_meta.py + tests/plausible/functional/test_health_check.py drafted with
EXTRA_ENV setting required Phoenix vars (DISABLE_AUTH, DISABLE_REGISTRATION, SECRET_KEY_BASE).
Stack converges 1/1 but the served app returns HTTP 500 from / for the full 600s HTTP_TIMEOUT
window — config-class failure, not a deploy-timing issue. Diagnosing needs live container-log
inspection + iterative env tuning, more debug cycles than fit autonomous mode. Committing the
draft + a DEFERRED.md entry; operator can iterate when they want.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-28 17:39:36 +01:00
9f2e120ec0 review(2): F2-10 CLOSED via DEFERRED.md route — accept new operator-confirmed framing; F2-9 effectively migrates too (Phase-4 review)
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-28 17:33:31 +01:00
8bafbd4968 status(2): Q4.4 ghost + Q4.8 uptime-kuma done; F2-10 closed via DEFERRED.md route
- STATUS-2: in-flight summarizes recipes shipped this sprint (Q3.1+Q3.4 partial; Q4.1+Q4.3+
  Q4.4+Q4.8 full); harness DEPLOY_TIMEOUT plumb-through; DEFERRED.md 9 open entries.
- BACKLOG-2: Q4.4 ghost + Q4.8 uptime-kuma checked off; F2-10 closed via DEFERRED.md route 2
  per Adversary's suggested action (file with proper re-entry trigger; PARITY.md no longer
  duplicates DEFERRED.md).
- tests/uptime-kuma/PARITY.md: 'Deferred' section now points to DEFERRED.md instead of
  duplicating the deferral text.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-28 17:25:25 +01:00
1bd7c7a1d3 feat(2): Q4.4 ghost + DEPLOY_TIMEOUT plumb-through for heavy recipes
Harness change (small, surgical):
- runner/harness/lifecycle.deploy_app gains a deploy_timeout param (default 900s); passes
  through to abra.deploy(timeout=...). For heavy recipes (ghost, matrix-synapse, lasuite-meet),
  the orchestrator + dep resolver now read recipe_meta.DEPLOY_TIMEOUT and pass it so the Python
  subprocess wrapping abra deploy doesn't SIGKILL it before the recipe's INTERNAL TIMEOUT
  (via EXTRA_ENV) finishes swarm convergence.
- runner/run_recipe_ci.py + runner/harness/deps.py: thread recipe_meta.DEPLOY_TIMEOUT into
  the per-recipe deploy_app call.

Q4.4 ghost enrollment:
- recipe_meta.py: HEALTH_PATH=/, DEPLOY_TIMEOUT=1200 (subprocess), EXTRA_ENV={TIMEOUT: 1200}
  (recipe internal). Ghost cold-start with theme + DB migration runs ~12-15min on cc-ci.
- functional/test_health_check.py: GET / returns 200 (themed site).
- functional/test_content_api.py: GET /ghost/api/content/settings/ returns 200 (settings JSON)
  or 401/403 (Ghost error envelope) — distinguishes ghost-server up + JSON API working from
  static fallback.
- functional/test_admin_redirect.py: GET /ghost/ returns 200 or 302 + Ghost branding;
  proves admin route is wired through nginx proxy.
- PARITY.md: recipe-maintainer corpus has no ghost tests/, Phase-2 health_check is the
  parity baseline; create-a-post deeper test deferred (DEFERRED.md, --extra-tests linked).

Cold-verifiable (log /root/ccci-q44-ghost-r3.log):
  RECIPE=ghost STAGES=install,custom cc-ci-run runner/run_recipe_ci.py
  install + 3 functional tests PASS, deploy-count=1. 28/28 unit tests still PASS.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-28 17:23:40 +01:00
44e88f3750 deferred(2): hygiene — move 5 Phase-2 entries from under '## Closed deferrals' to '## Open deferrals'
Per orchestrator note: my prior append (commit 650ab47) accidentally landed under the
'## Closed deferrals' header instead of '## Open deferrals'. All 5 entries (lasuite-docs OIDC
parity, cryptpad create-a-pad, uptime-kuma create-a-monitor, ghost create-a-post, authentik
enrollment) are still OPEN (unchecked boxes) — section relocation only, no content change.

'## Closed deferrals' restored to its (none yet) placeholder.
2026-05-28 17:10:28 +01:00
1ae23598e7 review(2): F2-8 CLOSED (bluesky goat+post round-trip cold-verified); F2-10 NEW (uptime-kuma §4.3 floor bypass — same pattern, DEFERRED.md migration suggested)
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-28 17:06:20 +01:00
650ab47fea deferred(2): migrate Phase-2 deferrals to DEFERRED.md with re-entry triggers (per orchestrator)
Per orchestrator note: machine-docs/DEFERRED.md is now the single canonical registry for any
deliberately-deferred work. Every entry MUST carry a specific RE-ENTRY TRIGGER. The orchestrator
seeded 4 matrix-synapse entries; this commit migrates the other Phase-2 deferrals I'd buried
in JOURNAL/PARITY/DECISIONS:

- lasuite-docs OIDC parity ports + create-a-doc (re-entry: before any Q3 gate claim — Adversary
  already flagged this in Q3/Q4 checkpoint).
- cryptpad create-a-pad + content round-trip Playwright (re-entry: Adversary F2-9 conditional —
  MUST lift before Phase-2 DONE; Q5.2 cold-sample must include).
- uptime-kuma create-a-monitor via Socket.IO (re-entry: --extra-tests flag OR another recipe
  needing Socket.IO).
- ghost create-a-post round-trip (re-entry: --extra-tests flag OR Q4 deeper-test pass before
  Phase-2 DONE).
- Q2.2 authentik enrollment + setup_authentik_realm backend (re-entry: when cryptpad oidc_login
  parity lifts — uses authentik — OR Phase-2 DONE review).

All linked to IDEAS.md --extra-tests flag where relevant. Phase-4 cleanup pass MUST review this
file per plan.md §6.1.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-28 17:00:49 +01:00
1aaf3bd4b8 feat(2): Q4.8 — uptime-kuma Phase-2 enrollment + 3 tests cold green
Recipe-maintainer corpus has no uptime-kuma tests/ directory (uptime-kuma wasn't in their parity
suite), so PARITY.md documents Phase-2 health_check as the parity-aligned baseline + 2 specific
tests beyond.

- tests/uptime-kuma/recipe_meta.py: HEALTH_PATH=/ accepts 200 or 302 (setup-wizard redirect).
- tests/uptime-kuma/functional/test_health_check.py: GET / returns 200/302.
- tests/uptime-kuma/functional/test_socketio_handshake.py: GET /socket.io/?EIO=4&transport=polling
  returns Engine.IO open packet (body starts with 0{, JSON has sid+pingInterval). Proves the
  real-time backend is wired through the nginx proxy.
- tests/uptime-kuma/functional/test_spa_branding.py: GETs /; asserts 'kuma' brand + SPA-bundle
  asset references (/assets/, /icon.svg, /favicon, main.) in the rendered HTML.
- Plan §4.3 prescribed 'create-a-monitor + list-it' deferred (Q4 follow-up — needs Socket.IO
  client + setup-wizard flow; substantial harness addition). PARITY.md documents the deferral.

Cold-verifiable: ssh cc-ci 'RECIPE=uptime-kuma STAGES=install,custom cc-ci-run runner/run_recipe_ci.py'
  install + 3 custom tests PASS, deploy-count=1.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-28 16:35:06 +01:00
3f6f10e239 fix(2): F2-8 — bluesky-pds account+post round-trip via goat CLI + atproto XRPC (Adversary cold)
Per REVIEW-2 ## Q3/Q4 partial checkpoint, F2-8: 'goat CLI in container / account state cleanup'
was the §7.1-prohibited 'needs X' excuse class (same shape as F2-4). The recipe-maintainer
corpus literally calls the goat CLI via abra app run — it works fine.

Added tests/bluesky-pds/functional/test_account_and_post.py:
- goat pds describe → assert did:web:<live_app> in output (PDS self-identifies correctly).
- goat pds admin account create with UUID-suffixed handle + email + per-run password (class-B);
  parse new account's did:plc:<id>.
- POST /xrpc/com.atproto.server.createSession with the new handle+password → accessJwt.
- POST /xrpc/com.atproto.repo.createRecord (collection=app.bsky.feed.post) with a UUID-marker
  text → returns at://<did>/app.bsky.feed.post/<rkey>.
- GET /xrpc/com.atproto.repo.getRecord with that rkey → assert value.text == marker (round-trip).
- Best-effort goat account delete cleanup in finally.

This is the §4.3 prescribed test in full (create account + create post + fetch back + delete).
Cold-verifiable: ssh cc-ci 'RECIPE=bluesky-pds STAGES=install,custom cc-ci-run runner/run_recipe_ci.py'
  install + 4 functional tests (health_check + describe_server + session_auth + account_and_post)
  all PASS, deploy-count=1.

PARITY.md updated to show goat_account.py as ported.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-28 16:28:45 +01:00