# IPZ E2E

Audience: AI coding agents first.

## Canonical rule

This file is canonical source for `international-press-zone` E2E process. Use it for every browser E2E run. MUST update this file and `.claude/commands/ipz-e2e.md` in same change whenever E2E topology, wrapper, stack script, fixtures, credentials, readiness, command, artifact path, or safety policy changes.

## Execute only remote

1. Start from plugin root.
2. Invoke E2E only through `~/.claude/bin/e2e-remote`.
3. `e2e-remote` starts `tests/e2e/remote-stack.sh` and Playwright on same authorized buildbox. `remote-stack.sh` installs locked admin dependencies with direct resolved `npm ci`, runs `npm run build`, and validates `admin/dist/asset-manifest.json` before it creates or mounts WordPress.
4. Set `WP_BASE_URL=http://127.0.0.1:8080` for client process.
5. Run Chromium and Firefox. NEVER skip either browser.

```bash
# plugin root
~/.claude/bin/e2e-remote \
  --server "tests/e2e/remote-stack.sh" \
  --wait-port 8080 \
  --env WP_BASE_URL=http://127.0.0.1:8080 \
  -- /home/user/.local/share/mise/installs/pnpm/11.5.2/pnpm \
    --dir tests/e2e \
    exec playwright test --config=playwright.config.js <focused-spec> \
    --project=chromium --project=firefox
```

Replace `<focused-spec>` with existing focused spec path relative to `tests/e2e`. Client MUST use exact absolute pnpm path, `--dir tests/e2e`, and `--config=playwright.config.js`; NEVER use `sh -lc`, Bun, `cd`, or `tests/e2e/playwright.config.js`. This pnpm path/version MUST update when environment changes. Stack resolves executable absolute `npm` and `node` paths with `command -v`, invokes resolved `npm` directly (never shell/Bun), and requires `admin/package-lock.json`; dependency resolution is `npm ci`, not `npm install`. Do not pin npm/node versions unless both this contract and stack enforce that exact version.

## Topology and safety

`tests/e2e/remote-stack.sh` runs isolated MariaDB, WordPress, official wordpress.org ACF, deterministic fixture, and candidate plugin on same authorized buildbox as Playwright. Stack binds only `127.0.0.1:8080` on that buildbox.

- MUST use localhost-only target: `http://127.0.0.1:8080`.
- NEVER use workstation proxy.
- NEVER run local browser/dev-server pair.
- NEVER send browser, E2E, smoke, or diagnostic request to `dev1.danzigeronline.com`.
- Target safety permits loopback or exact dedicated-host allowlist entries only. NEVER use wildcard, suffix, broad domain, empty-host, or bypass setting.
- NEVER repeat ad-hoc proxy experiments. Stop and use failure ladder.

Official wordpress.org ACF provides local field registration. Fixture graph assertions MUST prove exact repeater/flexible metadata consumed by IPZ. This scope does NOT claim ACF Pro input rendering.

## Lifecycle and readiness

`e2e-remote` owns server lifecycle. It launches `tests/e2e/remote-stack.sh`, waits for port `8080`, then starts Playwright. Server MUST remain foreground until client ends.

Port `8080` is withheld until all readiness gates pass: candidate `admin/package-lock.json`, exact `npm ci`, candidate `npm run build`, valid `admin/dist/asset-manifest.json` with required existing `main` and `editor` assets, DB, WordPress core, `wp-config.php`, WordPress install, deterministic fixture, ACF activation, candidate plugin activation, fixture graph assertions, WP-CLI postname permalink provisioning with writable Apache `.htaccess`, rewrite flush, and REST JSON routing smoke. Port open means every gate passed; it does NOT mean stack boot merely started.

Fresh stack MUST provision `/%postname%/` before delayed port publication. `remote-stack.sh` MUST use WP-CLI to create writable `/var/www/html/.htaccess`, set the postname structure, and flush rewrite rules. Before publication, `/wp-json/` MUST return `application/json` and JSON through Apache routing. HTML from `/wp-json/` is rewrite provisioning failure, NOT product JSON bug. If target plugin route requires authentication, smoke WordPress `/wp-json/` index plus rewrite routing; do NOT weaken gate with authenticated client state.

Stack script owns isolated Podman runtime. `8080` is fixed one-capacity slot per buildbox user: acquire nonblocking per-user `flock` before **any** Podman network, volume, or container mutation. Lock failure MUST report conflict and exit before creating resources. NEVER claim concurrent runs can execute in parallel.

Each run MUST generate unique sanitized resource identity. Apply exact ownership label `io.presszone.ipz-e2e.run=<RUN_ID>` to every created container, network, and volume. After fixed-port `flock` acquisition, before installing `EXIT`/`INT`/`TERM` cleanup and before every Podman mutation, inventory exact target names and exact requested ownership label namespace. Any target-name collision, label collision, or inventory-query failure MUST fail closed with zero deletion and no cleanup trap. This includes caller-reused `IPZ_E2E_RUN_ID`: preserve existing resources.

Cleanup MUST inventory resources by exact current invocation label before deletion, delete only names returned by that inventory, then re-inventory exact label namespace. NEVER remove caller-derived names without inventory proof. Every inventory query failure, removal failure, or surviving labeled resource MUST make cleanup and final process exit nonzero. Do not inspect, delete, attach to, retain, or reuse another run's resources.

Fixture is required. NEVER mark missing fixture as skipped, expected, or non-blocking. Fixture graph assertion failure blocks readiness and Playwright. Fix fixture/stack before test retry.

## Evidence gates

- Run focused spec in Chromium and Firefox.
- Focused run MUST contain zero skipped tests. Any `test.skip`, skipped test result, Chromium skip, or Firefox skip fails run.
- Record exact discovered and executed test count. Do not use approximate, aggregate-only, or omitted count receipt.
- Keep Playwright failure trace, screenshot, and video in `tests/e2e/test-results/`.
- Keep HTML report in distinct `tests/e2e/playwright-report/`; it MUST NOT equal raw-artifact directory.
- Keep JUnit receipt at `tests/e2e/test-results/junit.xml`.
- Resolve runtime warnings, console errors, page errors, and server errors. Clean runtime output required.
- Confirm cleanup query found zero containers, networks, and volumes with current-run ownership label.
- Real stack smoke passed on 2026-08-11 through `e2e-remote` on an authorized buildbox: ready after 32s; login, ACF activation, and candidate activation passed. Do NOT claim focused browser journey passed from this smoke.

## Failure ladder

Stop at first failing rung. Do not proxy around it.

1. **Concurrent port conflict:** fail without resource mutation. Preserve other run. Wait for owner cleanup, verify no current-run resources exist, then rerun canonical command.
2. **Server fails before port:** inspect `e2e-remote` server output and `remote-stack.sh` prerequisite/readiness failure. Confirm plugin root, resolved direct npm/node entrypoints, `admin/package-lock.json`, successful `npm ci` and `npm run build`, required existing `admin/dist/asset-manifest.json` assets, Podman availability, fixture path, candidate entrypoint, ACF activation, candidate activation, fixture graph assertions, permalink provisioning, `.htaccess`, and `/wp-json/` JSON smoke, then rerun canonical command.
3. **`No tests found`:** preserve remote topology. Verify `<focused-spec>` exists relative to `tests/e2e`; preserve exact client seam; rerun canonical command.
4. **Zero group disclosures with valid REST fixture:** fail run. First confirm remote stack built candidate `admin/dist` and manifest assets passed; inspect browser-loaded candidate assets and console errors. NEVER treat fixture REST success as SPA-build proof or add conditional test pass.
5. **Missing or invalid `admin/dist` manifest/assets:** fail before WordPress mount. Fix candidate admin dependencies, lockfile, or build; rerun canonical command. NEVER locally build or commit generated `admin/dist`.
6. **Skip, fixture, graph assertion, or REST routing failure:** fail run. Restore/fix deterministic fixture or routing provisioning. NEVER add skip, conditional pass, alternate host, or proxy.
7. **Runtime failure:** collect `test-results`, `playwright-report`, trace, screenshot, video, JUnit, browser/server output; fix source or test contract; rerun both browsers through canonical command.

## Receipt

Record: command; buildbox wrapper use; server script; target; focused spec; Chromium result; Firefox result; exact discovered/executed/skipped test counts; artifact paths; runtime-warning status; unique run identity; port-conflict status; permalink/rewrite/REST JSON smoke result; cleanup result; commit SHA. State failures and unresolved blockers exactly.
