diff --git a/machine-docs/REVIEW-2.md b/machine-docs/REVIEW-2.md index 86fdb8c..38f6477 100644 --- a/machine-docs/REVIEW-2.md +++ b/machine-docs/REVIEW-2.md @@ -1737,3 +1737,75 @@ upgrade/backup/restore tiers were not in this scoped run (install,custom only) plausible still ride the normal gate path if/when claimed; this probe targeted the §4.3 floor that was my standing obligation. No VETO. NOTE: ClickHouse boot is intermittently flaky on the single node (1-in-2 here) — a real env-fragility worth a retry/readiness margin if plausible runs go in CI rotation. + +## Q4.4 ghost — PASS @2026-05-30T06:57Z (COLD, first-hand, my clone /root/adv-verify @origin/main c60d5b5) + +Cold full-lifecycle re-run from my OWN clone — the exact claimed command — PLUS a negative control. +Logs `/root/adv-ghost-pr1.log` (PR=1, the fix) and `/root/adv-ghost-pr0-neg.log` (PR=0, published). + +**Primary — PR=1 (recipe-PR `recipe-maintainers/ghost#1`, REF=6d6227f7), all 5 tiers GREEN:** +- RUN SUMMARY: `deploy-count = 1 (expect 1)`; `install/upgrade/backup/restore/custom` **all pass**. + No cold-init flake on my run (install passed first try; ENV-NOTE retry not needed). +- Upgrade: `head_ref=6d6227f7 chaos-version=6d6227f7+U version=1.1.1+6-alpine→1.3.0+6.21.2-alpine` + (HC1, real prev→PR-head crossover; the `+U` untracked-overlay marker correctly tolerated by the + `a7e2af4` fix — which I reviewed: it strips ONLY the working-tree marker and still requires the + commit to equal head_ref, so HC1 is preserved, not weakened). +- `tests/ghost/test_upgrade.py::test_upgrade_preserves_state PASSED`, + `test_backup.py::test_backup_captures_state PASSED`, + `test_restore.py::test_restore_returns_state PASSED` (MySQL `ci_marker='original'` read back), + `functional/test_post_roundtrip.py::test_create_post_roundtrip PASSED` (6s). +- Clean teardown: post-run no ghost stack; 0 ghost secrets / 0 volumes / 0 networks. + +**P4 — the headline crux — restore PROVEN non-vacuous via NEGATIVE CONTROL (decisive).** Re-ran the +SAME overlay against the **published** recipe (`PR=0`, no fix), STAGES=install,backup,restore: +- `tests/_generic/test_restore.py::test_restore_healthy PASSED` (app healthy after restore) **but** + `tests/ghost/test_restore.py::test_restore_returns_state FAILED` — + `RuntimeError: docker exec … failed (rc=1) … ERROR 1146 (42S02) … Table 'ghost.ci_marker' doesn't + exist`. RUN SUMMARY: `restore : fail` (install+backup pass). +- Confirms: (a) the published ghost recipe's restore is a **silent no-op** — it ships a mysqldump + `--tab` backup pre-hook but **no `backupbot.restore.*` reimport hook**, so the dropped table never + returns (looks healthy, data lost — the immich#1 / mattermost-lts#1 class); (b) the P4 overlay is + **non-vacuous** (health-only passes here, the data-integrity assertion catches it); (c) it fails + **LOUD** — `exec_in_app` RAISES on a failed exec, never a silent `''`; (d) `ops.pre_restore` DROPs + `ci_marker` AND asserts the drop took (information_schema count=0), so a no-op restore is observable + in-band on EVERY run too. recipe-PR #1 (`ci/mysql-backup`) adds the reimport-on-restore hook → + `test_restore_returns_state` PASSES (PR=1). The recipe-PR is a **genuine fix**, verified end-to-end + by running both halves myself. + +**P3 — §4.3 create-post is REAL (read the body), closes the standing ghost §4.3 floor:** +`test_create_post_roundtrip` waits for the Admin API → bootstraps the owner (`/authentication/setup/`) +→ establishes a real cookie-aware admin **session** (`_ghost.GhostAdmin` builds a urllib opener with +an HTTPCookieProcessor + the CSRF `Origin` header Ghost requires) → POSTs a **published** post with a +unique-per-run marker in title+body (`/posts/?source=html`) → GETs it back by id (`?formats=html`) → +asserts BOTH the title and the body-html marker round-trip. Per-run UUID marker ⇒ no stale/echo +false-pass; exercises DB-write + Admin-API + publishing path. This **replaces the weak** +`test_content_api` (which accepted 401/403/400) as the §4.3 floor — my standing DONE-blocker #3 for +ghost is CLEARED. (`test_admin_redirect`, `test_content_api`, `test_health_check` also PASS as +supporting liveness.) + +**P2 N/A** (no recipe-maintainer corpus — documented in `tests/ghost/PARITY.md`). **P5/P6 N/A** — +postgres/MySQL is in-recipe (no external dep); core publishing exercised via the Admin API; no +browser-only UX owed. **P7** — no weakened/skipped/mocked tests. The two cc-ci infra changes are +legitimate, NOT test-weakening: (1) `compose.ccci-health.yml` start_period overlay gives Ghost's +~6-9min fresh MySQL migration time to finish so the healthcheck doesn't kill it mid-migration +(`migrations_lock` deadlock) — a test-harness fixture for a real slow-cold-boot, the migration itself +is genuine; (2) the `+U` HC1 fix (reviewed above, preserves the commit match). + +**Break-it checks:** (1) PR=0 negative control → restore RED on the published recipe (teeth proven); +(2) clean teardown after a **FAILED** run — post-PR=0 node fully clean (no ghost residue); (3) per-run +unique post marker defeats stale-response false-pass; (4) in-band pre_restore drop+assert-took; (5) +deploy-count=1 (no hidden redeploy). ENV fragility noted: ghost's mysql:8.0 cold-init healthcheck is +flaky (Builder saw one install timeout pr1c → passed on retry pr1d); my PR=1 install passed first try. + +**Verdict: Q4.4 ghost PASS.** Full lifecycle GREEN cold, deploy-count=1, real upgrade crossover +1.1.1+6-alpine→1.3.0+6.21.2-alpine, P4 MySQL ci_marker survives backup→restore (non-vacuous, proven +by PR=0 negative control), §4.3 create-post real (closes the ghost §4.3 floor), clean teardown (incl. +after failure). No `## VETO`. Advances P1 coverage (ghost full green). recipe-PR +`recipe-maintainers/ghost#1` is a real restore fix — 4th data-loss-class recipe bug cc-ci has caught +(immich, mattermost-lts, ghost; bluesky's volume restore was already sound). **My standing ghost §4.3 +DONE-blocker is CLEARED.** + +**Isolation note:** verdict from the plan (P1–P8) + the test code (ops.py / test_{restore,backup, +upgrade}.py / functional/{_ghost,test_post_roundtrip}.py) + the `a7e2af4` HC1 diff + the STATUS +Gate-Q4.4 verification info + my own cold PR=1 full run AND PR=0 negative control. JOURNAL-2 not +consulted before this verdict.