<!-- AUTO-GENERATED by scripts/wi-claude-rules-sync.sh — do not edit. -->
<!-- bundle: 20-qa-skill | sources: 3 .mdc files -->
---
name: qa
description: WI Smart QA, screenshots, registration smoke before user test
---

<!-- source: .cursor/rules/wi-screenshot-hygiene.mdc -->

# WI Screenshot Hygiene

- Store new local QA screenshots in `.cursor-wi-screenshots/` at repo root.
- Treat older rule references to `ss/ss-*.png` as legacy examples; prefer `.cursor-wi-screenshots/ss-*.png` for new screenshots.
- Keep screenshot files local only. Do not commit them unless Tobias explicitly asks for a forced add.
- Everything needed only for agent work stays local: credentials, env files, screenshots, hooks, ad-hoc scripts, and helper notes. Only plugin/repo-intended files are deployed or committed.
- Clean `.cursor-wi-screenshots/*.png` and legacy `ss/*.png` daily; delete screenshots older than 24 hours.
- A screenshot only counts as QA evidence when the target state is actually visible and measurable. If the page still shows loading dots, empty known-late content, a 100ms warning/intermediate state, third-party widgets that load after first paint (for example tawk.to), or a similar transient state, wait up to 15 seconds and take another screenshot.
- Repeat the wait/screenshot loop until the target content appears or roughly 15 seconds have passed. If the target is still not visible or measurable after about 15 seconds, do not claim the fix is verified: say explicitly that you could not see the target and Tobias must confirm manually. Never attach a screenshot as proof when the element under test is missing.
- The Cursor stop hook runs the cleanup at most once per local day and writes its marker to `.cursor-wi-screenshots/.last-cleanup`.

## Smart QA Kosten-Politik (Kanon ab 2026-07-06, Tobias)

**Grundsatz:** Der Agent darf **unbegrenzt viel** Smart QA (L0–L4) laufen lassen, wenn es sinnvoll ist. Das ist **kostenlos** (Shell + lokales Playwright, JSON stdout, keine Bild-Tokens, kein MCP).

| Stufe | Kosten | Wann / wie oft |
|-------|--------|----------------|
| **L0–L4** | Kostenlos | Nach **jedem** Deploy, volle Matrix (`npm run qa:smart:matrix`), mehrere Contracts, Re-Runs bis grün — **kein Limit** |
| **L5** | Kostenlos (Disk) | Nur wenn L0–L4 `ok:false` — Fail-PNG lokal |
| **L6** | Bild-Tokens | Nur **eine** Fail-PNG Read pro URL, um Fehler zu verstehen / selbst zu fixen |
| **L7** | MCP + Tokens | Nur wenn L6 unklar, Interaktion noetig, oder wirklich nicht anders geht |

**„Bin ich richtig?“** → immer zuerst `WI_SMART_QA_RESULT` (JSON). **Bezahlte Vision** (L6/L7) nur bei Fehlern, zur Fehleranalyse, oder wenn kein JSON-Check reicht.

**Smoke-Matrix:** `npm run qa:smart:matrix` ist Default nach Journey-/UI-Deploy. Baselines: `npm run qa:smart:baselines` (lokal, `wi-dev/tests/snapshots/`).

## Smart QA Escalation (Kanon ab 2026-07-06)

**Default nach Deploy:** `npm run qa:smart -- --contract scripts/qa/contracts/<phase>.json --headless` oder `npm run qa:smart:matrix`. Agent wertet `WI_SMART_QA_RESULT=…` aus (JSON, kein Bild).

**Pflicht-Reihenfolge (L0–L7):**

| Stufe | Was |
|-------|-----|
| L0–L4 | curl, Text/DOM, Fingerprint, Pixel-Diff, pHash/axe — lokal via [`scripts/wi-smart-qa.mjs`](../../scripts/wi-smart-qa.mjs) |
| L5 | Local Playwright PNG — [`wi-visible-target-screenshot.sh`](../../scripts/wi-visible-target-screenshot.sh) `--headless` nur bei Fail |
| L6 | Local Vision — Agent **Read** auf genau **eine** Fail-PNG |
| L7 | MCP Browser — nur wenn L6 unklar oder Interaktion noetig |

**Verbot:** MCP (`browser_*`) vor L5+L6. **Verbot:** Read PNG wenn `WI_SMART_QA_RESULT.ok === true`.

## Escalation — when Smart QA / local target incomplete (Pflicht)

**Before claiming QA green**, check: does `WI_SMART_QA_RESULT` show `ok:true` OR does the Fail-PNG show the **exact element or state**?

If **no** — escalation order:

1. `npm run qa:smart` / contract pass again with `--wait-ms 15000`
2. Playwright **headless** L5 (`wi-visible-target-screenshot.sh --headless` or Smart QA fail PNG)
3. **L6 Local Vision:** Read **one** Fail-PNG (cropped preferred)
4. **L7 MCP Browser** — only if L6 unclear or tab/click needed
5. **Headed Google Chrome** (`wi-visible-target-screenshot.sh` ohne `--headless`) — nur Notfall oder **23:00–05:00**

Login für wp-admin: Credentials aus Env (z. B. `.env.wicom-staging.local`) im **Cursor-Browser** einmal einloggen, Session wiederverwenden — nicht pro Task neues Fenster.

Optional: `--mobile 375x812` for a second PNG (`-mobile` suffix). Examples:

| Touchpoint | Example `--selector` |
|------------|----------------------|
| tawk side tab | `#tawk-min-container,.tawk-min-container,#tawk-bubble-container` |
| WI grid Register button | `.wi_grid_container a.wi-grid-register-btn` |
| Sidebar toggle (live) | `.sidebar-toggle-container,.cta-overlay-show-icon` |

Cross-ref: [`wi-deliverable-loop.mdc`](wi-deliverable-loop.mdc), [`wi-agent-workflow.mdc`](wi-agent-workflow.mdc) Schritt 4.

## Chat — klickbar in Cursor (hart)

- `.cursor-wi-screenshots/*.png` **darf nicht** in `.cursorignore` stehen (nur `.gitignore`) — sonst öffnet Klick im Chat nichts.
- Pro Screenshot im Chat: eingebettet `![…](.cursor-wi-screenshots/ss-….png)` **und** klickbarer relativer Link `[.cursor-wi-screenshots/ss-….png](.cursor-wi-screenshots/ss-….png)` — **kein** `file://`.

## Cursor — große QA-PNGs direkt öffnen (Tobias 2026-07-05)

Hochauflösende Screenshots (0.5–5 MB+) dürfen **nicht** hinter „File is large → Load file“ hängen.

**Global (User settings, nicht Repo):** `~/Library/Application Support/Cursor/User/settings.json`

```json
"workbench.editorLargeFileConfirmation": 0
```

`0` = Bestätigung aus, PNG öffnet direkt im Editor/Vorschau. Nach Änderung: betroffene PNG-Tab schließen und erneut aus Chat/Explorer öffnen (ggf. **Developer: Reload Window**).

Agent: bei QA-Screenshots **keine** Größenreduktion nur wegen Cursor-Load-Dialog — Auflösung für Admin/TN-Checks beibehalten.

## Screenshot Sinn-Check — Claim vs. Bild (Pflicht, Tobias 2026-07-05)

**Regel:** Ein PNG ist nur Beweis, wenn **Behauptung** und **sichtbarer Inhalt** übereinstimmen. URL-Slug allein reicht nicht (Reg-Seite und Live-Raum können dieselbe Permalink-Basis nutzen).

### Loop (hart, vor „gefixt und geprüft“)

1. **Ziel benennen** (1 Satz): z. B. „Live-Raum-Preview Kampagne 76“, nicht nur „nicht thank-you“.
2. **URL + Session prüfen** (Shell/`curl -I` oder Browser-URL nach Redirect):
   - Live-Raum-Preview braucht `live` + `webinar` + `preview=true` in der **finalen** URL.
   - **Admin-Preview** braucht eingeloggten Host (`wi_user_can_bypass_wi_funnel_guest_redirects`) — sonst Redirect auf Reg-LP ohne Query (`wi_maybe_redirect_guest_unauthorized_live_room_preview` in `inc/wi-frontend-functions.php`).
3. **Screenshot** → PNG **selbst** ansehen (Read-Tool oder Cursor-Vorschau), nicht nur Dateiname.
4. **Checkliste** (Touchpoint unten): alles „Muss sichtbar“ ja, alles „Darf nicht“ nein?
5. **Nein** → Ursache notieren (Gast-Session, Redirect, falscher Tab, Ladezustand) → Schritt 2–4 wiederholen. **Nicht** als grün melden.
6. **Ja** → erst dann PNG im Chat + Link; Dateiname `ss-<task>-<touchpoint>.png`.

### Touchpoint-Matrix (Auszug)

| Behauptung | Muss im PNG sichtbar sein | Darf **nicht** dominieren |
|------------|---------------------------|---------------------------|
| **Live-Raum-Preview** | Player/Stream-Fläche, Live-UI (Chat/Q&A/CTA je nach Template) | „Register now“, Reg-Formular, reine LP-Headline |
| **Thank-You-Preview** | Countdown/TY-Bestätigung, `wi-ty-*` / ticketCDArea | Reg-Formular |
| **Registrierung** | Opt-in-Formular, Submit-CTA | Live-Player Vollbild |
| **Admin Tab X** | Nav-Highlight = Tab X + passender Panel-Titel/Inhalt | Anderer Tab-Inhalt |

### Lesson P48 / `ss-p48-room-preview-cursor-browser.png` (2026-07-05)

- **Behauptet:** Room-Preview nach URL-Fix (nicht thank-you-Slug).
- **Tatsächlich:** Registrierungs-LP — „Register now“, kein Live-Raum; `curl -I` zeigte **302** auf URL **ohne** `live=1&webinar=1&preview=true` (anonymer Cursor-Browser, kein wp-admin-Login).
- **Fehler:** Slug ≠ TY als Erfolg gewertet; PNG nicht gegen „Room preview“ geprüft.
- **Richtig für Room-Preview-QA:** zuerst wp-admin im Cursor-Browser einloggen → Preview-Link aus Tab Video **oder** finale URL mit Live-Query → PNG: Player sichtbar, kein Reg-Formular → `curl -I` ohne Redirect der Live-Parameter.

Code-URL-Fix (Resolver) kann stimmen (`wp eval`), **visueller** Room-Preview-Beweis bleibt separat — Gast-Session liefert absichtlich Reg-LP.

---

<!-- source: .cursor/rules/wi-auto-test-fix.mdc -->

# WI — Auto-Test dein Fix (Staging)

Nach jedem Deploy auf Cloudways und **vor** Chat-Abschluss: curl-Matrix laufen lassen, HTTP-Status + Redirect-Ziel prüfen, optional NDJSON-Log ziehen. Kein "fertig" ohne runtime evidence (Header + ggf. Log).

## Curl-Autotest (Reg → TY → Webinar-Raum)

`--max-redirs 0` zeigt die erste Redirect-Stufe. `-A` setzt einen erkennbaren User Agent für Log-Korrelation.

```bash
URL_TY='https://wordpress-1301098-5039235.cloudwaysapps.com/talha-and-tobias-thank-you/?lid=<valid-lid>&live=&webinar='
URL_LIVE='https://wordpress-1301098-5039235.cloudwaysapps.com/talha-and-tobias/?live&webinar&lid=<valid-lid>'
URL_TY_NO_LID='https://wordpress-1301098-5039235.cloudwaysapps.com/talha-and-tobias-thank-you/'
URL_TY_BAD_LID='https://wordpress-1301098-5039235.cloudwaysapps.com/talha-and-tobias-thank-you/?lid=deadbeefdeadbeefdeadbeefdeadbeefdeadbeef'
URL_REG_SC='https://wordpress-1301098-5039235.cloudwaysapps.com/wi-38-reg-custom-2/'
URL_REG_GB='https://wordpress-1301098-5039235.cloudwaysapps.com/talha-and-tobias-register-now-3/'

for U in "$URL_TY" "$URL_LIVE" "$URL_TY_NO_LID" "$URL_TY_BAD_LID" "$URL_REG_SC" "$URL_REG_GB"; do
  echo "URL: $U"
  curl -sS -o /dev/null -D - -A 'WIQA/1.0' --max-redirs 0 "$U" | head -n 6
  echo "---"
done
```

## Erwartete Matrix (Master Switch = Live, gültiger Lead vorhanden)

| Fall | Erwartet |
| --- | --- |
| TY + gültiger `lid` (live/webinar) | `302` → `…/<webinar-slug>/?live&webinar&lid=…&watch_type=live` |
| Direkter Live-Link | `200` (Webinar-Raum) |
| TY ohne `lid` | `302` → Default-Reg-Page |
| TY mit invalid `lid` | `302` → Default-Reg-Page |
| SC-Custom-Reg-Page | `200` (Formular lädt) |
| GB-Reg-Page | `200` (Formular lädt) |

Nicht-302 auf TY-Fällen vor Startzeit bei Master Switch = Live ⇒ Fix greift nicht.

## Log-Verifikation (optional, empfohlen nach Fixes im Redirect-Flow)

Server-Log zu Workspace ziehen und nach Marker greppen:

```bash
scp cloudways-wi-staging:public_html/wp-content/plugins/webinarignition-premium/.cursor/debug-af21ca.log \
  .cursor/debug-af21ca.log
rg -n 'master_switch_live_tr_redirect|thank_you_guest_redirect_registration|bail_not_canonical_webinar_page' \
  .cursor/debug-af21ca.log
```

Erwartet bei TY+lid+live: NDJSON-Zeile `message:"master_switch_live_tr_redirect"` mit `room_len>0`.

## JS/AJAX-Flows (Reg → TY) — PFLICHT: Live-Browser via Playwright-MCP

Reg-Redirect passiert per JS nach AJAX-Lead-Anlage. curl prüft das nicht. Automatisieren:

1. `browser_navigate` auf die Reg-Page.
2. `browser_evaluate` Inputs setzen und `#optinBTN` clicken — Seiten können **mehrere** `form.wi-optin-form` enthalten (pro `[wi_webinar_block]`). Vorher per `browser_evaluate` die Form-Map auslesen (`wi_webinar_id`, `wi_webinar_type`, `wi_webinar_ty_url`) und gezielt die richtige Kampagne submitten.
3. `browser_wait_for { time: 6 }` → Final-URL prüfen. Erwartet:
   - Master Switch = Live: `.../<webinar-slug>/?live&webinar&lid=<40-hex>&watch_type=live`.
   - Master Switch = Countdown (nicht live): `.../<ty-slug>/?lid=<40-hex>&live=&webinar=&code=`.
4. Server-Log `debug-af21ca.log` ziehen; erwarteter NDJSON-Eintrag `lead_lookup_result` mit `lead_empty:false, lead_id_len:40`.

Stolperfallen (Regressionsschutz):
- `leadId`-Regex muss 40-Hex vor Ziffern matchen — sonst werden hashes wie `367306abc…` auf `367306` gekürzt.
- `initialWebinarignition.addQueryArg` muss bestehende Params ersetzen, nicht anhängen (verhindert `?lid=x&lid=x&webinar=&webinar=`).
- `is_lead_protected` entscheidet ob numerisch oder hash zurückgegeben wird; beide Pfade dürfen kein „lead_empty“ am TY-Lookup produzieren.

## Abbruch-Regel

- Kein "fertig"-Chat ohne curl-Matrix-Ergebnis.
- Bei Abweichung: Instrumentation behalten, neue Hypothesen, erneut deploy + Autotest.
- Erst nach Autotest-Erfolg Screenshot-Selbsttest (siehe `wi-staging-infra.mdc`) + `working ✓`-Freigabe (siehe `wi-git-clean-between-tasks.mdc`).

---

<!-- source: .cursor/rules/wi-real-registration-smoke-before-user-test.mdc -->

# WI — Real Registration Smoke Before User Test

Bei jeder Änderung an Registration, Thank-You, Live-Redirect, Webinar-Join-URL, E-Mail-Validation oder Formular-Submit gilt:

1. Tobias darf erst testen, nachdem der Agent selbst eine echte anonyme Real-Matrix geprüft hat.
2. Debug-URLs, Admin-Preview, reine Shortcode-Proben oder Localhost zählen nur zusätzlich, nie als Ersatz.
3. Pro Matrix-Fall echte veröffentlichte Teilnehmer-URL öffnen, sichtbare Formularfelder füllen, echte Mail-Domain mit MX nutzen, Button klicken, Redirect/Thank-You/Live-Raum prüfen.
4. Pflicht-Matrix:
 - HC Live
 - HC Evergreen
 - GB Live
 - GB Evergreen
 - Custom/Shortcode real page
5. Pro Fall Screenshot vor Submit und nach Redirect speichern unter `.cursor-wi-screenshots/`.
6. Abschluss erst mit getesteten Links, Screenshot-Links und klarer Trennung: grün / offen / blockiert.
7. Falls eine Seite unveröffentlicht ist, keinen grünen Ersatz melden: als blockiert notieren und zusätzlich eine andere reale veröffentlichte Seite desselben Typs testen.

Volle Default-Matrix inkl. Live/Replay/Ended + alle 5 Server-Typen: [wi-modern-room-journey-smoke-default.mdc](wi-modern-room-journey-smoke-default.mdc).

