# GOLIVE receipt — lane R9 — PG-36 rollback artifact and procedure

- **Gate**: PG-36 — "A rollback artifact and procedure exist for the exact launch version; a failed rollout can restore the last known-good plugin without corrupting schema/content."
- **Date**: 2026-08-22
- **Verdict**: WORKS — reversed upgrade proof passed on debian1; procedure documented in `docs/launch/ROLLBACK.md`.

## Artifacts

| Role | Artifact | SHA-256 |
|---|---|---|
| Launch candidate (rolled back FROM) | `international-press-zone-0.9.84.zip` | `507f747aac02e93df9e8da6007d12ff01d6103454bc3c89e543e622755e40c22` (byte-identical to the PG-33/PG-34 artifact) |
| Last known-good (rolled back TO) | `international-press-zone-0.9.74.zip` | `5720476d82f96796de9d639d61195745e7f528916ff8ca62f3776499962c5716` (dev1-deployed artifact, provenance per PG-34 receipt) |

## Proof

Reversed run of the landed PG-34 spec (`tests/e2e/upgrade-zip.spec.js`) on the sanctioned remote stack (debian1, `IPZ_STACK_NO_PLUGIN_MOUNT=1`): install+activate 0.9.84 → seed customer post, plugin options (`ipz_feature_flags` + marker option), and an `ipz_workflow_states` row → replace with 0.9.74 via wp-admin "Replace current with uploaded" (`overwrite=downgrade-plugin`) → verify plugin row shows 0.9.74 active, admin SPA boots, seeded post title/content and option values byte-exact, seeded workflow row still present (existence of the seeded translation ID), zero PHP/JS console errors.

- Final result: **`1 passed (1.0m)`** (run 3).
- Env: `IPZ_OLD_ZIP_PATH=0.9.84.zip, IPZ_OLD_VERSION=0.9.84, IPZ_ZIP_PATH=0.9.74.zip, IPZ_EXPECTED_VERSION=0.9.74`.

## Iteration history (honest findings)

1. **Run 1 — FAIL.** plugins.php returned HTTP 500 ("critical error") immediately after the replace click; Playwright retries then failed at seed on `Duplicate entry '987654321' for key 'idx_translation'` (seed not idempotent across retries).
2. **Run 2 — FAIL, fatal captured.** Re-run with pinned container name and post-failure `podman logs` capture. Fatal: `Uncaught Error: Class "InternationalPressZone\Translation\TranslationProtectionService" not found in .../includes/Core/Plugin.php:318`. Diagnosis: the fataling `Plugin.php` is **0.9.84's** file (line 318 matches byte-for-byte; 0.9.74 never references that class) and the Apache log shows plugins.php was requested while the `overwrite=downgrade-plugin` request was still mid-swap. This is a **transient swap-window race** (non-atomic file replacement + opcode cache), NOT a 0.9.74-on-0.9.84-state incompatibility. Static cross-check: 0.9.74's migration runner is downgrade-safe on a 1.2.8 schema (`version_compare(..., '<')` gate + `CREATE TABLE IF NOT EXISTS`), so the newer schema is left untouched.
3. **Fixes** (this lane): spec waits for the overwrite result page (`overwrite=` URL + WP core success string, verified verbatim against `class-plugin-upgrader.php`) before navigating — this race also existed in the forward PG-34 direction, which had passed by luck; mu-plugin seed deletes the marker `translation_id` row before insert (retry idempotency). The transient mid-swap 500 is documented as an operator expectation in `ROLLBACK.md` step 4.
4. **Run 3 — PASS** (`1 passed (1.0m)`).

## Review

- sol/low (report-only) round 1: rejected — 2 blocking findings in `ROLLBACK.md` (re-proving recipe not executable as written; workflow-row claim overstated vs the existence-only oracle). Both fixed.
- sol/low re-verify: `{"approved": true, "blocking_findings": []}`.
- Known oracle limitation (advisory, accepted): workflow-row readback verifies existence of the seeded translation ID, not a field-by-field value comparison.

## Scope caveat

The proof covers the exact launch pair 0.9.84 → 0.9.74. `ROLLBACK.md` mandates re-running the reversed spec for every future launch pair before relying on rollback.
