<!-- AUTO-GENERATED by scripts/wi-claude-rules-sync.sh — do not edit. -->
<!-- bundle: 20-deploy-qa | sources: 9 .mdc files -->
---
paths:
  - "webinarignition.php"
  - "scripts/**"
  - ".cursor-wi-screenshots/**"
---

<!-- source: .cursor/rules/wi-agent-workflow.mdc -->

# WebinarIgnition Premium v5 — Agent-Workflow

Legacy-Code (teilweise ab 2013) neben modernem WordPress: bei jeder Änderung **vorsichtig**, **kleiner Scope**, **PHP-Lint + Deploy + Screenshot-Loop** wie unten. Infra (SSH, URLs, Befehle): **`wi-staging-infra.mdc`**. Freemius `is_premium`: **`cloudways-freemius-premium-qa.mdc`** (Kanon: `true` im Repo und auf Staging; Ausnahme nur expliziter Free-Build-Test).

**Kurz-Checkliste (nicht auslassen):** Schnellreferenz *bestes Coden + Risiko* → **`wi-development-guardrails.mdc`** (Abschnitt **Coding safety cheatsheet**). Zusätzlich zur kompakten Regel **`wi-deliverable-loop.mdc`** gilt: Änderungen an **Webinar-Listen**, Admin-Listen oder jeder Staging-sichtbaren Oberfläche **immer** mit **Cloudways-Deploy + Cache-Flush + headless Screenshot der Prüf-URL** absichern; eine abschließende „fertig“-Melding **erst** mit **vollständiger https-URL** und **Screenshot(s)** im Chat (siehe Tabelle unten). Nicht mit „bitte selbst deployen/prüfen“ beenden, wenn Deploy machbar ist.

## Pflicht-Ablauf nach jedem Task (automatisch bis vor Commit)

### Cursor-Modell bei Blockade (Tobias)

- Wenn ein Task **nach mehreren sinnvollen Iterationen** (z. B. Fix → Deploy → Screenshot → nächste Hypothese) **keine belastbare Lösung** liefert oder die Ursache **fachlich zu schwierig** wirkt: **im Chat kurz notieren**, z. B. `⚠️ Blockade — bitte Agent-Modell für #Axxx hochstufen (Haiku → Sonnet, ggf. Opus), danach wieder Haiku`.
- **Tobias stellt in Cursor manuell** das Agent-Modell um (z. B. **Haiku → Sonnet**, bei Bedarf **Opus**), lässt **denselben Task** dort weiterlaufen, setzt danach wieder auf **Haiku** (oder sein Standard) zurück.
- Der Agent kann das Modell **nicht** selbst umschalten; die Zeile ist das **explizite Signal** für dich und für Folge-Chats.

## Autonomie, Plan und Fragen (Tobias 2026-05-16)

**Plan go-ready (Kanon):** [`wi-plan-go-autonomous.mdc`](wi-plan-go-autonomous.mdc) — vor `go` globale Discovery (12 Domänen, Tabelle A/B/C/**★**), **Hybrid** (`AskQuestion` max. 1× nur **✗ BLOCKER**), Abschnitt **„Gutes Gefühl vor go"**, User-Review **~45 Min**; nach `go` kein Architektur-Nachfragen. Vorlage: `.cursor/plans/_TEMPLATE.go-ready.md`. Jeder Plan: **Agent & Model** (nur Modellname + Datum) — gleiches Format wie Task-Abschluss **Model**-Zeile.

**Standard**, wenn Tobias **„autonom"**, **„alle Phasen"**, **„go"** oder **„ohne Rückfragen umsetzen"** sagt — trotzdem **nicht raten** (Entscheidungen stehen im Plan):

| Situation | Agent-Verhalten |
|-----------|-----------------|
| **Mehr als ~3 verifizierbare Schritte** oder **>3 betroffene Dateien** (ohne explizite Freigabe) | **Go-ready Plan** (Discovery-Tabelle + P0…Pn + Prüf-URLs + Rollback). Optional `.cursor/plans/<task>.plan.md`. Nach User-**`go`**: Phasen **ohne erneute Freigabe** (Deploy + Screenshot pro sichtbarer Phase). |
| **Alle Schritte notieren** | Während der Arbeit: **Todo-Liste** (Cursor Todos) oder Plan-Phasen; am Ende: **nummerierte Prüfliste** für manuelles QA (links zu Staging-URLs). |
| **Fragen vor `go`** | Unklarheit in **Entscheidungstabelle** + ggf. **ein** `AskQuestion`-Batch nur für **✗** (URL, Lizenz, Copy, Business, Veto-**✗**, Secrets). **⚠** und **★** im Plan; quasi-gleich → **🤖** Agent-Entscheid (1 Zeile). |
| **Nicht fragen** (direkt ausführen) | PHP-Lint · Version-Bump · Deploy · Cache · Screenshot-Selbsttest · Option **A** nach Risk-Veto (Soft-Warn) · Trivial-Fix laut **`wi-protect-veto-alternatives.mdc`**. |

**Kommunikation:** Plan und Fortschritt **sichtbar** halten (z. B. „P2 erledigt → Deploy 4.10.x“), aber **kein** Stop nach jedem Teil-Schritt, sofern kein Hard-Stop.

**Abschluss autonomer Läufe:** wie Pflicht-Ablauf unten — **`✅ #Axxx gefixt und geprüft`** + Prüfliste + **`✅ #Axxx done — waiting for #Axxx working ✓`**; Git erst nach **`#Axxx working ✓`**.

### Sub-Go runner (Claude Code Pro — 2026-07-10)

Ship-set **implementation** via [`scripts/wi-sub-go.sh`](../scripts/wi-sub-go.sh), not ad-hoc `claude -p` strings. Cursor stays **Auto** for orchestration. Policy: [`wi-sub-go-model-policy.mdc`](wi-sub-go-model-policy.mdc).

| Step | Agent |
|------|-------|
| Before code (default) | `./scripts/wi-sub-go.sh --axxx #Axxx --prompt "…" --model auto --dirs inc,assets` |
| Plan / `go` / P0…Pn | same + `--plan` (or `--model opusplan`) |
| After failed deploy-QA | `./scripts/wi-sub-go.sh bump-fail --axxx #Axxx` then retry `--model auto` |
| Parse result | `WI_SUBGO_RESULT=…` JSON line (model, ok, log path) |
| Escape | User prefix `cursor-only:` — no Sub-Go |

After Sub-Go: unchanged lint → deploy → QA chain. On `WI_SUBGO_RESULT` fail: bump-fail + auto retry without asking Tobias.

---

**Projekt-Default:** Nach jeder relevanten Änderung **selbst** Cloudways-**Deploy**, **`wp cache flush`** und **Screenshot-Selbsttest** — nicht auf eine separate User-Aufforderung warten.

### Reihenfolge Kommunikation: erst Verifikation, dann Chat-Ausgabe

- **Keine** abschließende Task-Antwort („gefixt“, „fertig“, Zusammenfassung, URLs) **vor** erfolgreichem **Deploy + Cache + Screenshot(s) + Selbstauswertung** und — wo in **`wi-protect-veto-alternatives.mdc`** vorgesehen — **🧪 QA-Gate**, sofern Shell/Netzwerk/SSH verfügbar sind.
- Erst wenn der Screenshot (oder die Prüfung) **erfolgreich** ist **und** das **QA-Gate** (falls Pflicht) **kein ✗ bei QA Team** meldet: Nachricht mit **`✅ #Axxx gefixt und geprüft`**, **vollständigen https-Links**, **eingebetteten PNGs** (`![…](.cursor-wi-screenshots/ss-….png)`) und **Pflicht: klickbarem relativem Link** zur gleichen PNG — `[.cursor-wi-screenshots/ss-….png](.cursor-wi-screenshots/ss-….png)` — damit Tobias per Klick **in der Cursor-App** das Bild öffnet. Beide Zeilen pro Screenshot, keine Ausnahme. Kein `file://`. PNG-Ordner **nicht** in `.cursorignore` (nur `.gitignore`). **Erfolgs-Notiz für Folgeläufe:** kurz notieren, **welche** Verifikation grün war (Kommando, HTTP-Status, Screenshot-Pfad, Skriptname — **keine** Secrets); Details zu lokalen Credentials siehe **`wi-staging-infra.mdc`** (Abschnitte *Lokale Credentials in Cursor* und *Erfolgreiche Tests festhalten*).
- **Nicht** zuerst nur Code-Erklärung und „Cache leeren Sie selbst“ — der Agent führt Deploy und Screenshot selbst aus (Ausnahme: nachweislich fehlende Credentials/Netzwerk; dann Fehler dokumentieren).

### FSE Style-Variationen (Twenty Twenty-Five u. a.)

- **Frontend:** immer Staging-URL screenshotten (aktueller Zustand auf dem Server).
- **Helle + dunkle Presets** im **Site Editor → Styles** kann der Agent **ohne** eingeloggte Admin-Session nicht zuverlässig per Tool durchklicken. Wenn der Task **Footer, Linkfarben, Kontrast oder Registrierungs-Landing vs. Theme** betrifft: im Abschnitt **„Für dich zum Check“** explizit auffordern, im [Site Editor](https://wordpress-1301098-5039235.cloudwaysapps.com/wp-admin/site-editor.php) **zwei verschiedene Style-Variationen** zu wählen und dieselbe Frontend-Seite erneut zu prüfen.

### Browser-Console vor Fix + Debug-Log (Pflicht bei UI/JS)

Gilt **vor** Schritt 1, sobald der Task **Staging-sichtbares UI**, **WI-JS/CSS** oder **Participant-/Admin-Frontend** berührt. Ausnahme: reine PHP-Backend-/Cron-/Mail-Logik ohne Browser-Oberfläche.

- **Browser-Console (Admin + TN):** Relevante **Prüf-URL(s)** auf Staging öffnen (Playwright MCP / headless CDP / eingeloggter Admin-Flow) und **Admin-Browser-Console** bzw. **TN-Browser-Console** auf **Errors + Warnings** prüfen — wp-admin und Teilnehmer-Seite, wenn der Task beide betrifft.
- **Debug-Logs:** Wo verfügbar kurz mitlesen — `debug.log` (WP_DEBUG), WI-Telemetrie (z. B. vergessene **localhost**-Ingest/`127.0.0.1`-Fetch), **`console.log`-Spam** in WI-Assets (`assets/`, `inc/lp/js/`, Admin-JS).
- **Im gleichen Task mitfixen** (minimal diff, wenn Scope passt): Debug-Fetches zu localhost/`127.0.0.1` **komplett entfernen** (kein Ingest, kein `#region agent log` — siehe **`wi-no-localhost-debug-ingest.mdc`**), Guards für fehlendes **TinyMCE**, ungültige **iframe `allow`-Features**, überflüssige WI-`console.log`/`console.debug`.
- **Nicht jagen** (Out-of-scope, nur dokumentieren): 100ms-/HMS-iframe-Interna (@tldraw o. ä.), Browser-Extensions, Freemius/PayPal-CSP im Checkout-iframe, **JQMIGRATE**-Info — außer der Task ist **explizit** dafür.
- **Nach Deploy (Deliverable-Loop):** Hat der Task Participant-/Admin-Frontend angefasst, Browser-Console **erneut** prüfen — Ziel: **ruhiger** als vorher bzw. keine neuen WI-Fehler. Kurz in der Abschlussnachricht notieren (z. B. „Browser-Console: keine WI-Errors“ oder offene Rest-Warnung unter **Noch zu prüfen**).

Cross-Ref: [`.cursor/plans/live_console_cleanup_bc4ca49e.plan.md`](../plans/live_console_cleanup_bc4ca49e.plan.md) (Beispiele Admin-/TN-Browser-Console).

Führe die Schritte in der Tabelle **in dieser Reihenfolge** aus. **Nicht** „fertig“/„done“ sagen oder den Task als erledigt werten, bevor Schritt 2 grün ist und Schritt 3–5 erledigt sind — **Schritt 5** umfasst dabei das **QA-Gate** vor der ersten **`✅ #Axxx gefixt`**-Zeile (außer der User bricht den Ablauf ausdrücklich ab).

| Schritt | Was |
|--------|-----|
| 0 | **Browser-Console vor Fix + Debug-Log** — siehe Abschnitt oben; bei reinem Backend ohne Browser: „n/a“ |
| 1 | **Fix / Task umsetzen** — Änderungen im Repo |
| 2 | **PHP-Syntax-Check** — siehe **`wi-staging-infra.mdc`**; bei Fehler zuerst beheben |
| 2b | **JS-Syntax-Check** (wenn `assets/*.js` geändert): `node --check assets/webinarignition-admin-dashboard.js` — bei `SyntaxError` **kein** Deploy/Abschluss |
| 2c | **i18n-Check** (wenn User-Text/Copy): nur **Englisch** im Code, Domain `webinar-ignition`, Übersetzungen **Loco** — siehe Abschnitt *i18n-Check vor working ✓*; bei reinem CSS/Logik ohne Strings: „keine neuen Strings“ |
| 2d | **CSS Cross-Impact** (wenn `*.css` oder inline CSS geändert): Touchpoint-Scope + Kollateral — **`wi-css-change-impact.mdc`**; bei BLOCKER kein `waiting for working ✓` ohne `#Axxx css-impact ok` |
| 3 | **Deploy** — incremental SCP/SFTP (Cloudways: `cloudways-wi-staging` alias / `cursor2026-06-26@64.225.107.166`; wp2leads o. ä.: SFTP-User laut `WP2LEADS-ACCESS.local.md`); bei jedem Staging-sichtbaren Change **vorher** `webinarignition.php` **Plugin-Header `Version:` + `WEBINARIGNITION_VERSION`** gemeinsam bumpen, `webinarignition.php` **immer mit deployen**, Upload verifizieren; danach **`wp cache flush`** per SSH wo Shell verfügbar; **`is_premium`** siehe **`cloudways-freemius-premium-qa.mdc`** |
| 4 | **Smart QA + Screenshots** — **unbegrenzt** `npm run qa:smart` / `:matrix` (L0–L4, kostenlos). Volle HC-Matrix nach Journey-Deploy empfohlen. **PNG/Vision nur bei Fail:** L5 → L6 Read (1x) → L7 MCP nur zur Not. Happy Path: `WI_SMART_QA_RESULT.ok:true` ohne Read/MCP. Siehe **`wi-screenshot-hygiene.mdc`** (Kosten-Politik). |
| 5 | **Chat: Prüfung ausgeben** — zuerst **🧪 QA-Gate** aus **`wi-protect-veto-alternatives.mdc`** (wenn Pflicht), dann eingebettete Bilder, volle https-Links, klickbare Links zu PNGs; siehe Abschnitt **Task-Nummern** und **Abschluss-Format**. |
| 6 | **Abschluss** — **`✅ #Axxx gefixt und geprüft`**, Kurzbeschreibung, Links, Screenshots, **offene Tasks** (Abschnitt **Noch zu prüfen**), **`✅ #Axxx done — waiting for #Axxx working ✓`** (Copy/Paste-Zeile — Bestätigung z. B. **`#A763 working ✓`**), Zeile **Model**. |
| 7 | **`working ✓` (Task erledigt)** — **Kein** automatisches `git add` / commit / push. Nur: `plan-status.yaml` / Abschluss-Chat (`wi-working-complete.sh`, [`wi-plan-status.yaml.mdc`](wi-plan-status.yaml.mdc)). **Ship** getrennt: `push to Freemius`, `sync bitbucket` → vorher [`scripts/wi-recovery/wi-ship-preflight.sh`](../scripts/wi-recovery/wi-ship-preflight.sh). **Recovery-Ausnahme:** explizites `go Recovery` darf einmal committen. |

Zwischen 6 und 7: **kein Commit, kein Push** — auch nicht bei `working`.

### `working` vs Ship (ab Recovery #A920)

| Befehl | Agent macht |
|--------|-------------|
| **`#Axxx working`** / **`working ✓`** | Task **abgeschlossen** markieren (Plan-Status, Chat). **Kein Git.** |
| **`push to Freemius`** / **`sync bitbucket`** | Preflight → commit/push nur auf expliziten Ship-Befehl |
| **Multi-Agent** | [`scripts/wi-agent-file-guard.sh`](../scripts/wi-agent-file-guard.sh) + [`wi-agent-file-guard.mdc`](wi-agent-file-guard.mdc): kurzer Lock, `release-task` nach Batch, Stop-Hook raeumt ab |

### Autonomer Version-Bump vor Staging-Deploy

Ziel: Der Agent arbeitet autonom und liefert nur prüfbare Änderungen. Wenn eine Änderung auf Cloudways sichtbar oder testrelevant sein soll, gilt vor jedem Upload:

- **Staging-only (kein Freemius/wp.org):** Patch in der **aktuellen Zeile** hochzählen (`4.12.458` → `4.12.459`). Beide Werte identisch.
- **Freemius + wp.org publizieren:** **Minor-Zeile** hoch — nächste Release-Zeile **`4.13`**, nicht weiter `4.12.xxx` als großen Release. Format `4.13` ok. **`readme.txt` `Stable tag:`** mitziehen. Details + wann Bugfix-only in Patch bleiben: [`cloudways-freemius-premium-qa.mdc`](cloudways-freemius-premium-qa.mdc) *Versionierung*. Bei Unklarheit kurz fragen: 4.13 / 4.14 oder noch 4.12.x?

- **Immer mit hochladen:** `webinarignition.php` gehört in jede Staging-Upload-Liste, sobald PHP/CSS/JS/Templates oder sonstige Staging-sichtbare Dateien geändert wurden. Nicht nur die Einzeldatei per `scp` hochladen.
- **Warum:** Ohne neue Hauptversion bleiben WordPress-Plugin-Metadaten, Enqueue-URLs, Browser-/Page-Caches und ggf. OPcache oft auf dem alten Stand. Dann sieht der Screenshot-Test scheinbar keine Änderung und der Task ist nicht automatisch belegbar.
- **Verifizieren:** Nach SFTP/lftp muss `scripts/lib/wi-upload-verify.sh` bzw. ein gleichwertiger Check bestätigen, dass die remote `webinarignition.php` dieselbe `Version:` und denselben `WEBINARIGNITION_VERSION`-Wert wie lokal hat. Bei Mismatch: falscher Remote-Pfad, Teilupload oder PHP/OPcache-Verdacht klären, nicht abschließen.
- **Cache danach:** Erst nach erfolgreichem Upload/Verify `wp cache flush` ausführen und dann die Screenshot-Selbstprüfung starten.
- **Ausnahme:** Reine Doku-/Rule-/lokale Skriptänderungen ohne Staging-Sichtbarkeit brauchen keinen Versions-Bump, keinen Cloudways-Deploy und keinen Screenshot.

### Task-Nummern (intern)

- Jede abgeschlossene **Teil-Aufgabe** erhält eine fortlaufende ID: **`#A001`**, **`#A002`**, … (Format **`#A`** + mindestens drei Ziffern, führende Nullen).
- Abschluss beginnt mit **`✅ #A042 gefixt und geprüft`** (Beispiel) plus Kurzbeschreibung.
- Der User bestätigt mit **`#A042 working ✓`** (empfohlen) oder weiterhin **`working ✓`** für den aktuell genannten Block. Die Warte-Zeile **`✅ #A042 done — waiting for #A042 working ✓`** enthält die Task-ID zweimal — Copy/Paste der ganzen Zeile oder nur **`#A042 working ✓`** aus dem Suffix.
- **Keine Nummern wiederverwenden**; bei erneutem Fix derselben Sache: **neue** Nummer (`#A043`).
- Unteraufgaben optional: `#A042a`, `#A042b`.

### Model (jede Abschluss-Antwort + go-ready-Pläne)

Am **Ende** jeder abschließenden Task-Nachricht (nach Screenshots und Links) **eine Zeile** — **nur Modellname**, keine Tokens/Kosten im Chat (Kosten nur in der Cursor-Usage-Übersicht):

`💡 Model: <sichtbarer Modellname aus Cursor/Kontext>`

**Go-ready-Pläne** (`CreatePlan`, [`.cursor/plans/_TEMPLATE.go-ready.md`](../plans/_TEMPLATE.go-ready.md)): Abschnitt **Agent & Model** direkt unter H1 mit `💡 Model: … · Plan erstellt: YYYY-MM-DD`. Bei Plan-Übergabe im Chat dieselbe Zeile (nur Modell). Siehe [`wi-plan-go-autonomous.mdc`](wi-plan-go-autonomous.mdc).

### Abschluss-Format (Pflicht-Struktur)

0. **🧪 QA-Gate** (wenn Pflicht laut **`wi-protect-veto-alternatives.mdc`**) — **vor** Punkt 1; bei **✗ QA Team** nicht abschließen.

1. **`✅ #Axxx gefixt und geprüft`** — ein Satz: was geändert wurde.
2. Screenshot(s) — **pro Bild zwei Zeilen (Pflicht)**:
   - Eingebettet: `![…](.cursor-wi-screenshots/ss-….png)`
   - Klickbarer relativer Link (öffnet in Cursor): `[.cursor-wi-screenshots/ss-….png](.cursor-wi-screenshots/ss-….png)`
3. Vollständige **https://**-URL(s) zum Testen auf Staging — **alles über Cursor-Browser** (`browser_navigate`), Admin + öffentliche TN-URLs. **Kein** macOS `open "https://…"` tagsüber. Headed System-Chrome nur Notfall oder **23:00–05:00** (siehe **`wi-deliverable-loop.mdc`**, **`wi-screenshot-hygiene.mdc`**). Einmal einloggen, Session wiederverwenden.
4. **`### Noch zu prüfen`** (oder **„Für dich zum Check“**): alle **noch nicht** mit **`#A… working`** / **`working ✓`** bestätigten Punkte — jeweils mit **Test-Link**, ggf. Screenshot-Link, Zeile beginnt mit **`Noch zu prüfen:`** wo sinnvoll.
5. **`✅ #Axxx done — waiting for #Axxx working ✓`** — **`#Axxx`** in Schritt 1 und hier identisch; Suffix **`#Axxx working ✓`** direkt kopierbar.
6. Zeile **Model** wie oben.

Bereits bestätigte Tasks **nicht** erneut auflisten (kein Wiederholen von Bildern/Links zu erledigten `#A…`).

### Nach User `#Axxx working` / `working ✓` (Pflicht-Antwort)

Sobald Tobias **`#Axxx working`** oder **`working ✓`** schreibt — **nach** Git-Schritt 7 (pull → add → commit → push, nur allowlist) — **immer** diese Struktur (auch bei kleinen Tasks):

1. **`✅ #Axxx working bestätigt`** — ein Satz: was committed/erledigt gilt.
2. **`### Stand der Dinge`** — Tabelle aller noch-nicht-bestätigten Tasks (Pflicht, auch wenn leer mit Hinweis):

   | Status | Was | Wann |
   |---|---|---|
   | ✅ working | [`#Aabc — Titel`](Link zum Task/Plan/Anchor) | jetzt |
   | 📋 zu testen | [`#Adef — Titel`](Link) | heute/dieser Chat/2026-05-26 |
   | 📋 zu testen | [`#Aghi — Titel`](Link) | … |

   **Pflicht-Links pro Zeile** — jeder Task-Name in der „Was"-Spalte ist ein **klickbarer Markdown-Link**:
   - **Code-Task** (`#Axxx P1 — …`): Link auf Plan-Datei `.cursor/plans/aXXX_*.plan.md` oder auf die geänderte Quelldatei.
   - **Plan-Task** (eigener Plan): Link auf die Plan-Datei.
   - **Sub-Task aus laufendem Plan** (`#A238 P2b`): Link auf den Anchor/die Datei im Plan, z. B. `[#A238 P2b — Webhook async](.cursor/plans/A237_perf_fixes_diagnose_to_ship.plan.md)`.
   - **Idea/Backlog-Eintrag**: Link auf `.cursor/wi-current-ideas.md` oder `.cursor/wi-backlog.md`.
   - **Verify-URL** (z. B. Staging-Test): direkter https-Link.

   **NIE** Task-Namen ohne Link nennen. Wenn keine sinnvolle Ziel-Datei existiert → Plan-Stub in `.cursor/plans/` anlegen oder Anchor in `.cursor/wi-current-ideas.md` einfügen, **dann** verlinken.

3. **`### Offene Pläne (inkl. Sub-Aufgaben)`** — alle aktiven `.cursor/plans/*.plan.md` mit `pending` Todos:

   - **`[A237 perf fixes](.cursor/plans/A237_perf_fixes_diagnose_to_ship.plan.md)`** — Sub-Todos: `- [ ] p2b-webhook-async` · `- [ ] p5-admin-notice`
   - **`[A243 AS Version-Negotiation](.cursor/plans/a243_as_version_negotiation.plan.md)`** — Status: go-ready, wartet auf `go #A243`
   - …

   Format pro Plan: Link auf Plan-Datei + 1 Zeile pro `pending` Todo. **Alle** Sub-Todos einzeln auflisten (kein '+ N weitere'-Kürzen). Ziel (Tobias 2026-05-26): sofort entscheiden ohne Plan-Datei zu öffnen — auch bei 8+ Todos pro Plan.

4. **`### Nächstes Todo (Empfehlung)`** — **ein** passender Folge-Task:
   - **Titel** + **Ziel (1 Satz)** + **Warum jetzt** (Priorität, Abhängigkeit, Felix/Release).
   - **Plan-Link:** existierende Datei `.cursor/plans/<name>.plan.md` oder **Mini go-ready**.
   - **Start:** konkret z. B. „Sag: Plan für [Titel]" oder „`go` auf [Plan-Datei]".

5. **Plan komplett abgeschlossen** (alle Phasen + dieses `working ✓`): Abschlusszeile **`loco all`** — Hinweis auf `./scripts/loco-all.sh` nach Staging-Sync ([`wi-loco-all.mdc`](wi-loco-all.mdc)). Bei laufenden/offenen Plänen: weglassen.

6. Zeile **Model**.

**Quellen lesen vor jeder Antwort** (nicht raten): [`.cursor/wi-current-ideas.md`](../wi-current-ideas.md), [`.cursor/wi-backlog.md`](../wi-backlog.md), `.cursor/plans/*.plan.md` (YAML-Todos mit `status: pending`).

**Kein** leeres „Nächstes Todo" — wenn nichts sinnvoll folgt: explizit **„Nächstes Todo: Pause / du wählst Thema"** + trotzdem **Stand der Dinge** + **Offene Pläne** vollständig listen.

**Ziel der Struktur (Tobias 2026-05-26):** Fokus auf eine Sache, ohne dass etwas vergessen wird. Alles auf einer Seite, alles klickbar, ein Blick → entscheiden.

Cross-Ref Plan-Qualität: [`wi-plan-go-autonomous.mdc`](wi-plan-go-autonomous.mdc).

### Selbstprüfung (Pflicht)

- **Browser-Console-Loop:** Nach Deploy dieselbe Prüf-URL — Browser-Console ohne neue WI-Errors; sichtbare WI-Warnungen im Task mitfixen oder unter **Noch zu prüfen**.
- **Screenshot-Loop:** PNG **selbst auswerten** (Lesbarkeit, Buttons, Kontrast, Badges). Mängel → zurück zu Schritt 1, erneut Deploy, Cache, Screenshot.
- **Screenshot muss den Zielzustand wirklich zeigen:** Ein PNG ist nur ein Nachweis, wenn der relevante Zustand **sichtbar und eindeutig prüfbar** ist. Wenn der Screenshot den Zielzustand nicht zeigt (z. B. falscher Scrollstand, falscher Tab, Sidebar zu, erste statt letzte Chat-Nachrichten sichtbar), **nicht abschließen**. Stattdessen aktiv korrigieren: scrollen, Tab/Button klicken, warten, größeren/anderen Viewport nutzen, eingeloggten echten Browser-Kontext/Playwright-Session verwenden, Screenshot neu aufnehmen und erneut selbst prüfen. Abschluss erst, wenn das Bild das behauptete Ergebnis belegt.
- **wp-admin / Plugin-UI:** nicht allein `wp eval` — bei relevanten Tasks **immer** `STAGING-ACCESS.local.md` **finden und auslesen**, auf Staging **einloggen**, dann **eingeloggter** Screenshot der genannten Admin-URL (kein „fertig“ nur aus Code-Review ohne diese Kette, sofern Zugang/Netzwerk da ist).
- Nur bei **nachweislichem** Deploy-/SSH-Fehler: dokumentieren, was fehlschlug und was lokal geprüft wurde.


### Mobile-Screenshots (Pflicht bei TN-UI)

- **Nicht** Desktop-URL mit schmalem Viewport abschneiden und als „Mobile“ verkaufen.
- **Vorher** echte Mobile-Ansicht: Viewport **375×812** (oder Playwright `viewport`), ggf. `deviceScaleFactor: 2`, **4–6 s** warten (Countdown/Grid).
- Prüfen: Countdown-Boxen **vollständig** sichtbar (nicht rechts abgeschnitten), CTA tappbar.
- Dateiname z. B. `ss-*-mobile-375.png` — alter Desktop-Crop **nicht** als Mobile-QA zählen.

### PCP vor `working ✓` (Security-/Release-Tasks)

Wenn der Task **#A197**, PCP, Security oder Release betrifft: **vor** `waiting for working ✓` erneut:

```bash
./scripts/wi-pcp-staging.sh   # oder voller wp plugin check auf Staging
```

- Ergebnis in `scripts/reports/wi-pcp-rerun-summary.txt` (oder Summary im Chat) — **kein neues Feature** auf ungeprüftem Stand.
- Vergleich: EscapeOutput / ABSPATH / eval sollten **nicht wieder steigen**; Volllauf dient Regression, nicht „alles fixen“.

### i18n-Check vor `working ✓` (Pflicht bei User-Text / Übersetzungen)

**Kanon (Tobias 2026-05-17):** Übersetzungen laufen über **Loco Translate** (`.mo` auf der Site / `languages/loco/`). Im **PHP/JS-Code** sind Strings **nur auf Englisch** übersetzbar einbauen — keine festen DE/FR-Texte im Source, keine zweite Sprache parallel im Repo committen.

| Check | Pflicht wenn … |
|-------|----------------|
| Neue/geänderte sichtbare Strings | Nur **Englisch** im Code; Wrapper `__()`, `esc_html__()`, `_e()`, `esc_attr__()` mit Domain **`webinar-ignition`** |
| Keine Loco-Arbeit im Agent-Task | Agent **committet keine** `.po`/`.mo` — Übersetzung bleibt **Loco** auf Staging/Live |
| Pro-Webinar-Sprache | TN + Webinar-Admin-UI: weiter `WebinarignitionManager::webinarignition_set_locale( $webinar_data )` / `webinar_lang` — Loco liefert die `.mo` pro Locale |
| Abschluss-Zeile | Bei Text-Touch: in der Warte-Nachricht kurz **`i18n: EN source only, Loco for DE/…`** notieren |

**Vor User-`#Axxx working` / `working ✓`:** Wenn der Task Copy geändert hat → oben bestätigen (oder „keine neuen Strings“). Ohne diese Zeile kein Commit nach `working`.


### CSS Cross-Impact vor `working ✓` (Pflicht bei CSS-Diffs)

**Kanon:** [wi-css-change-impact.mdc](wi-css-change-impact.mdc) · Selektor-Prinzip: **so klein wie möglich, so groß wie nötig**.

| Check | Pflicht wenn … |
|-------|----------------|
| Touchpoint benannt | registration / thankyou / grid / footer / … |
| Wenn-du-so-machst + zieht-mit-sich | Im Chat als **`🎨 CSS Cross-Impact #Axxx`** vor `waiting for working ✓` |
| BLOCKER | Mehrere Touchpoints, `max-width:none`/`100vw` ohne Scope, 26rem-Kollision — bis `#Axxx css-impact ok` |

**Kein CSS:** `css cross-impact: n/a (no CSS files)`.

Siehe auch geplanter Fix **#A196** (Textdomain-Lader, Free + Premium Ordner, gleiche Domain `webinar-ignition`).

### Code-Qualität & Refactor (Legacy neben WordPress-Core)

Bei Berührung alter Pfade/Funktionen:

1. **Prüfen:** Ersetzt eine **WP-Core-API** (oder moderne WP-Funktion) das bisherige Muster klarer und wartbarer? (Beispiele: `wp_remote_get` statt `curl`/`file_get_contents` für HTTP, `wp_send_json_*` statt manuelles JSON-Echo in AJAX, `$wpdb->prepare` für SQL, Nonces/Capabilities, Transients, `wp_enqueue_script` / `wp_add_inline_script` statt losem Inline-JS wo sinnvoll.)
2. **Wenn ja und Risiko gering:** im **gleichen Task-Scope** refactoren (nur die **direkt betroffene** Stelle — kein unaufgeforderter Mega-Refactor).
3. **Im Abschluss** kurz erwähnen, **was** modernisiert wurde (`#Axxx`-Text).
4. Immer wieder **Schritt 2 + Deploy + Screenshot** nach substanziellen Refactors.

### Ignorierte/lokale Dateien schneller bearbeiten

- Wenn `ReadFile` / `ApplyPatch` bei ignorierten oder lokalen Dateien wie `dist/*`, `ss/*` oder `.env.*.local` mit Permission denied blockieren, nicht mehrfach wiederholen: Inhalt über den bestehenden Shell-Kontext prüfen und gezielt per Shell/Python ändern.
- Kein neues Terminal öffnen und keine GUI-Bestätigungsfenster auslösen, wenn der User den aktuellen Terminal-Kontext verlangt.

### Commit scope (plugin only)

Before `git add`: stage **only** paths from **`wi-commit-scope.mdc`** (Bitbucket, Freemius, wp.org, customer upload — same ship-set). Never commit `.cursor/`, `scripts/`, debug snippets, or access docs.

### Harte Regeln

- **Vor Commit/Push:** `git pull origin main`
- **NIEMALS** Commit/Push ohne User-**Ship-Befehl** (`push to Freemius`, `sync bitbucket`, Recovery-`go`) — **`working ✓` allein committet nicht**
- **NIEMALS** Chrome sichtbar — nur headless
- **NIEMALS** SCP mit `/home/...` — immer `public_html/...` (siehe Infra-Rule)
- **NIEMALS** `STAGING-ACCESS.local.md` oder Staging-Passwörter committen
- **NIEMALS** Task mit „bitte selbst deployen/prüfen“ beenden, wenn Deploy machbar ist
- Unklarheiten: nachfragen, nicht raten

### PHP im Editor (Referenz)

- Cursor User Settings: `"php.validate.executablePath": "/opt/homebrew/bin/php"`
- Terminal: `PATH` mit `/opt/homebrew/bin`, damit Agent-`php` funktioniert

---

<!-- source: .cursor/rules/wi-staging-infra.mdc -->

# WebinarIgnition — Staging-Infrastruktur (Cloudways)

**Staging-Frontend:** https://wordpress-1301098-5039235.cloudwaysapps.com/  
**Staging wp-admin:** https://wordpress-1301098-5039235.cloudwaysapps.com/wp-admin 
**WI General Settings (kanonisch, Unterstrich + tab):** https://wordpress-1301098-5039235.cloudwaysapps.com/wp-admin/admin.php?page=webinarignition_settings&tab=general — **nicht** `page=webinarignition-settings` (Bindestrich → 403). Code: `wi_get_admin_general_settings_url()`. **Öffnen:** Cursor-Browser (Login einmal, Session wiederverwenden) — **kein** `open`/headed Chrome tagsüber; siehe **`wi-deliverable-loop.mdc`**. 
**Credentials:** `STAGING-ACCESS.local.md` (Repo-Root, nur lokal, **nie** committen). Für Skripte/lftp: **`.env.cloudways-staging.local`** (Vorlage: `.env.cloudways-staging.local.example`, **nie** committen). WP-Admin-Browserchecks nutzen **`scripts/env/staging-wp-browser.local.env`**. Keine password-manager-/CLI-/password-manager SSH-agent-Aufrufe verwenden.

### Lokale Credentials in Cursor

- Keine password-manager CLI-Befehle und keine Secret-Reference-URLs verwenden.
- Lokale Secret-Dateien aus den `.example`-Vorlagen kopieren, Werte lokal eintragen und nie committen.
- Wenn SSH/SCP einen password-manager key prompt öffnet: abbrechen und Passwort-SFTP/lftp oder einen normalen lokalen SSH-Key nutzen.

### Erfolgreiche Tests festhalten (nächster Lauf & Plugin-Check)

- War ein Check **grün** (Deploy, SSH, Node-Relay, REST-Probe, Admin-Browser, Screenshot): in der **Abschlussnachricht** eine **kurze Erfolgs-Zeile** — **was** verifiziert wurde und **wie** (z. B. `POST …/wi-signal` → 200, `./scripts/wi-staging-relay-full-probe.sh` → ok, `./scripts/run-staging-admin-huhner-preview.sh` → `ss/ss-huhner-preview-admin.png`). **Ohne** Passwörter und ohne Secret-Reference-URLs im Chat.
- Dasselbe Muster beim **Plugin-Review** / Release-Smoketest wiederholen, damit Regressionen schneller auffallen.

**Deploy & Selbsttest — Site-URL sichtbar machen:** Steht die Prüf-URL fest (Default oben, oder **vom User genannte** Test-/Preview-URL), gehört sie **nach jedem relevanten Deploy** in die Agent-Antwort: **vollständiger https-Link** (und Screenshot wo vorgesehen). Wenn der User **zum manuellen Test wartet**, nicht nur „Cache geleert“ — immer **konkret welche URL** er öffnen soll (ein Klick, keine Suche). Siehe auch **`wi-deliverable-loop.mdc`**.

### Pflichtkette — Credentials → Login → Screenshot (Admin / Plugin-UI)

Gilt für jeden Task, der **wp-admin**, **Plugin-Listen** oder andere **eingeloggte** Oberflächen betrifft (z. B. Unlinked SC, Webinar-Listen, Einstellungen):

1. **Finden:** Zuerst `STAGING-ACCESS.local.md` im Repo-Root; bei Fehlen nicht raten — User um Zugang bitten.
2. **Auslesen:** Lokale Datei lesen (Passwörter **nicht** vollständig in Chat/Commits pasten).
3. **Einloggen:** Auf Staging-**wp-admin** einloggen (Playwright MCP / Browser-Flow laut Projekt), Session steht.
4. **Prüfen:** Konkrete **Admin-Prüf-URL** öffnen → **Screenshot** nach `ss/ss-….png` → PNG **selbst** kurz prüfen (erwarteter Zustand).

Reine **öffentliche** Frontend-Prüfung: weiterhin anonymes headless Chrome wie unten — sobald **Capabilities** oder Admin-UI im Scope sind, **ohne** diese Kette kein „fertig“.

### Agent-Autonomie mit lokalen Dateien

- `STAGING-ACCESS.local.md` und `.env.*.local` sind lokale, gitignorierte Quellen. Falls ein Tool sie wegen Ignore-Regeln nicht lesen kann, lokale Env-Datei per Shell `source` laden oder den User um die fehlende lokale Datei bitten.
- **Eingeloggter Grid-/Shortcode-Check:** erst `https://wordpress-1301098-5039235.cloudwaysapps.com/wp-admin/`, danach die konkrete Frontend-Seite. Standard: `set -a; source scripts/env/staging-wp-browser.local.env; set +a; ./scripts/run-staging-admin-grid-test.sh`.

**Lieferpfad:** Nach Deploy immer **Screenshot-Selbsttest** der Prüf-URL; abschließende Agent-Antwort erst mit **https-URL + Screenshot(s)** — siehe **`wi-deliverable-loop.mdc`** und **`wi-agent-workflow.mdc`** (gilt explizit auch für **Webinar-Listen** und Admin-Listen).

## Server

- SSH Host: `64.225.107.166` Port `22`
- SSH Alias (Agent-Default): `cloudways-wi-staging` (`~/.ssh/config`, user `cursor2026-06-26`, key `~/.ssh/cloudways_cursor_20260626_rsa`) — verified 2026-06-28 for `cd public_html && wp ...`
- Direct SSH user for non-alias contexts: `cursor2026-06-26`
- WP Root (absolut): `/home/1301098.cloudwaysapps.com/ynhgztghtf/public_html/`
- Plugin (absolut): `/home/1301098.cloudwaysapps.com/ynhgztghtf/public_html/wp-content/plugins/webinarignition-premium/`
- **Aktives Plugin (Free vs Premium):** Vor jedem incremental Deploy / loco all per SSH prüfen welcher Slug aktiv ist (`webinar-ignition` vs `webinarignition-premium`). Nur in **`public_html/wp-content/plugins/{aktiver-slug}/`** deployen — siehe `wi-dev/local/scripts/lib/wi-staging-active-plugin.sh` und [`wi-loco-all.mdc`](wi-loco-all.mdc). Nach Slug-Wechsel: Plugin-Stand + `languages/` in den **neuen** Ordner legen; sonst wirkt UI „halb übersetzt“ (careless-switch).

**SCP-Ziel (relativ, Chroot-sicher):** `cloudways-wi-staging:public_html/wp-content/plugins/webinarignition-premium/`
- SFTP/lftp bleibt ok über `.env.cloudways-staging.local` (`cursor` user + Passwort); keine Secrets in Chat/Commit.

## Kontext Repo

- Lokaler Ordner: `~/Documents/webinarignition-bitbucket/`
- Bitbucket Remote: `git@bitbucket.org:WP-Leads-Plugins/webinarignition.git`

## Cloudways — Deploy-Präferenzen

- **Incremental:** nur geänderte Dateien per SCP/rsync nach `webinarignition-premium/` — nicht bei jedem Kleinkram den ganzen Tree.
- **Kanonische Pfade = PHP-`require`/`include`:** Auf dem Server muss der **exakte relative Pfad** existieren, den der Code lädt (z. B. `…/webinar-live-video-content-hunderedms.php`). Nur eine Datei unter Namen wie `*__premium_only.php` hochzuladen, **ohne** die vom `require` erwartete Datei `…hunderedms.php` mitzuliefern, erzeugt einen **Fatal error** und oft **abgeschnittenes HTML** (Symptom: nur Header, grauer Block, kein Player). Fix: immer den **kanonischen Repo-Dateinamen** deployen **oder** den Loader im Code anpassen — nicht nur auf dem Server umbenennen.
- **Full replace** nur bei erstem Aufspielen, sehr vielen Änderungen, Vendor/Node-Updates oder auf User-Anweisung.
- **Freemius:** Kanon **`cloudways-freemius-premium-qa.mdc`** — `'is_premium' => true` im Repo und auf Staging; Ausnahme nur expliziter Free-Build-Test.
- **Sicherheit:** keine Staging-Passwörter in Commits.
- **SCP-Ziel:** relativ `public_html/...`, **nie** `/home/...` als SCP-Pfad.

## Schritt 2 — PHP-Syntax-Check (immer vor Deploy)

Im Projektroot. Wenn `php` im PATH fehlt: `/opt/homebrew/bin/php` statt `php`.

```bash
cd ~/Documents/webinarignition-bitbucket
find . -name "*.php" \
  -not -path "./.git/*" \
  -not -path "./vendor/*" \
  -not -path "./node_modules/*" \
  | xargs -I{} php -l {}
```

Bei Fehlern: beheben und Check wiederholen. Erst danach Deploy.

## Schritt 3 — Deploy (incremental)

**Versions-Bump (Pflicht):** Wenn derselbe Code auf eine Site geht, die **bereits dieselbe** `Version:` / `WEBINARIGNITION_VERSION` hat, erkennt WordPress kein Plugin-Update — **Enqueue-URLs und manche Caches** bleiben störrisch. Vor dem SCP daher **`webinarignition.php`** anpassen: Plugin-Header `Version:` **und** `define( 'WEBINARIGNITION_VERSION', … )` **gemeinsam** um eine Patch-Stufe erhöhen (z. B. `4.07.89` → `4.07.90`). `webinarignition.php` **immer mit** in die Upload-Liste aufnehmen, wenn andere PHP-Dateien deployt werden.

Pro geänderte Datei (Repo-Root = `~/Documents/webinarignition-bitbucket`):

```bash
scp -P 22 inc/wi-frontend-functions.php \
  cloudways-wi-staging:public_html/wp-content/plugins/webinarignition-premium/inc/wi-frontend-functions.php
```

(Analog für jede andere geänderte Datei: gleicher Pfad unter `webinarignition-premium/`.)

**Wichtig:** Kein `scp file1 file2 ... remote:plugin-root/` fuer Dateien aus Unterordnern. `scp` legt dabei **keine** Unterordnerstruktur an und kopiert Basenames in den Plugin-Root. Stattdessen jede Datei einzeln mit exakt passendem Remote-Pfad hochladen (oder `rsync`/`lftp mirror` struktur-erhaltend nutzen). Nach Korrektur-Uploads bei Verdacht Root-Duplikate wie `wi-frontend-functions.php` im Plugin-Root entfernen und Upload-Pfade verifizieren.

Cache:

```bash
ssh cloudways-wi-staging "cd public_html && wp cache flush"
```

**Node / Realtime-Relay (curl + optional WP):**

- Nur Relay-HTTP: `./scripts/wi-node-relay-smoke.sh` oder `set -a; source scripts/env/node-relay-smoke.local.env; set +a; ./scripts/wi-node-relay-smoke.sh` — **`chmod` immer eigene Zeile**, kein `#`-Kommentar direkt hinter `chmod` (Paste-Fallen in zsh).
- Staging gesamt (Node + **WordPress REST mit Application Password** + optional **SSH `wp eval`**): Vorlage `scripts/env/staging-relay-probe.local.env.example` nach `scripts/env/staging-relay-probe.local.env` kopieren, lokale Werte setzen, dann `set -a; source scripts/env/staging-relay-probe.local.env; set +a; ./scripts/wi-staging-relay-full-probe.sh`.

**Deploy ohne SSH (Fail2ban / Port 22):** `chmod +x scripts/lftp-sync-plugin-staging.sh` → `./scripts/lftp-sync-plugin-staging.sh` (liest `.env.cloudways-staging.local`, lädt per SFTP dieselben Pfade wie oben). Dateiliste im Skript bei Bedarf erweitern. Wenn `webinarignition.php` dabei ist, lädt das Skript die Datei danach erneut herunter und vergleicht `Version:` + `WEBINARIGNITION_VERSION` mit dem lokalen Repo (`scripts/lib/wi-upload-verify.sh`); `WI_UPLOAD_VERIFY=0` schaltet das ab. Live-Upload: `./scripts/wp2leads-live-sftp.sh` — gleiches Verifikationsmuster; bei `WI_WP2LEADS_FILES` ohne `webinarignition.php` wird die Hauptdatei standardmäßig **automatisch vorangestellt** (`WI_WP2LEADS_ALWAYS_INCLUDE_MAIN_PHP=1`), damit Version+Verify nicht vergessen werden.

## Schritt 4–5 — Headless Screenshot + Selbstprüfung

**Admin-Session → Webinar-Preview (Playwright):** bevorzugt lokale Repo-Installation: `npm install` (einmalig) → `npm run pw:install` (einmalig/bei Browser-Update) → `set -a; source scripts/env/staging-wp-browser.local.env; set +a; npm run staging:admin:huhner` — schreibt `ss/ss-huhner-preview-admin.png` (lokal, gitignored). Direkter Runner bleibt ok: `set -a; source scripts/env/staging-wp-browser.local.env; set +a; ./scripts/run-staging-admin-huhner-preview.sh`; er nutzt `node_modules/playwright` und installiert nur als Fallback temporär. Vorlage kopieren: `staging-wp-browser.local.env` (in `.gitignore`).

**Kein** sichtbares Chrome-Fenster — nur `--headless=new`.

`URL` = **task-spezifische** Prüf-URL (vom User oder Fallback unten).

```bash
cd ~/Documents/webinarignition-bitbucket
CHROME=$( (which google-chrome || which chromium-browser || \
  echo "/Applications/Google Chrome.app/Contents/MacOS/Google Chrome") 2>/dev/null | head -1 )
URL="https://wordpress-1301098-5039235.cloudwaysapps.com/testing-the-live-reg-webinar/?confirmed&live=&lid=e52670c95b494350f944a9cc7f69a69085967d5a&code="
"$CHROME" --headless=new --disable-gpu --no-sandbox --hide-scrollbars \
  --window-size=1440,900 --screenshot=ss/ss-verify-desktop.png "$URL" 2>/dev/null
"$CHROME" --headless=new --disable-gpu --no-sandbox --hide-scrollbars \
  --window-size=375,812  --screenshot=ss/ss-verify-mobile.png "$URL" 2>/dev/null
```

**WI Webinar-Grid (z. B. `…/grid-2/`):** Karten und „Register“-Buttons werden per Frontend-Logik nachgeladen. Vor dem Screenshot **4–6 Sekunden warten** (z. B. Playwright nach `networkidle`: `await page.waitForTimeout(5000)`), sonst wirken Button-**Text**farbe oder Layout noch nicht final.

**Mobile-Screenshot:** Zeigt der feste Viewport (z. B. 375×812) das zu prüfende Objekt (Grid, CTA) **nicht** oder nur angeschnitten (lange Seite, Grid unter dem Fold), dann **Full-Page-Screenshot** (`fullPage: true`) und/oder **höheres Viewport** (z. B. 375×1600) bzw. gezielt auf `.wi_grid_container` scrollen — sonst kein belastbarer QA-Nachweis.

**Lange wp-admin-Seiten:** Kleine Schrift in Full-Page-PNG wirkt unscharf bei schmalem Viewport. Mit **Playwright MCP** `browser_run_code`: nach Login `page.context().newCDPSession(page)` → `Emulation.setDeviceMetricsOverride` mit **breitem Viewport** (z. B. 2560×1400) und **`deviceScaleFactor: 2`–`2.5`**, dann `page.screenshot({ fullPage: true, scale: 'device', path: '…/ss/ss-….png' })`. Optional Ausschnitt der Listen-Region (z. B. `.wi-webinar-wrap`).

**Screenshots:** Ordner **`ss/`** im Repo-Root (z. B. `ss/ss-verify-desktop.png`); in **`.gitignore`** (`/ss/*.png`) — lokal QA, nicht committen (außer bewusst `git add -f`).

## Test-URLs Staging (anpassen wenn der User eine andere URL gibt)

**Prüf-URL — Priorität (hart):**

1. **User-URL zuerst:** Nennt Tobias in der Nachricht eine **konkrete https-URL**, ist diese die **Prüf-URL** für Selbsttest, Screenshot-Beweis und Chat-Abschluss — nicht ohne Absprache durch einen anderen Default ersetzen.
2. **Live vs Evergreen passend:** Arbeit an **Live** (Webhook, 1-Click Live, Live-TY, Zeitzone, Live-Raum) → **Live-HC** bzw. die im Task genannte **Live-Kampagnen-URL** verwenden (siehe HC-Matrix: Live id=8). **Evergreen-HC** (`testing-the-live-reg-webinar/`, id=7) nicht als Ersatz-Smoke für Live-only Tasks nutzen, sofern der Task nicht ausdrücklich Evergreen ist.
3. **Fallback:** HC-Matrix unten bzw. `wi-staging-hc-test-webinars.md`.

**HC-Test-Matrix (kanonisch):** [.cursor/wi-staging-hc-test-webinars.md](../wi-staging-hc-test-webinars.md) — Evergreen **Kampagne id=7** (`testing-the-live-reg-webinar`), Live **Kampagne id=8** (`2025-11-15-tobias`).  
**Warum alte Beispiele oft „ID 3“ suggerieren:** URLs wie `registration-1-for-webinar-3/` oder `123-register-now-3/` sind **WordPress-Seiten-Slugs** (historischer Name), nicht zwingend die aktuelle **WI-Kampagnen-ID** in der Datenbank. Nach Restore/Neuaufbau kann **Kampagne `id=3` fehlen** — für QA die HC-Matrix nutzen.

- **Evergreen HC (Reg + TY, id=7):**  
  Reg: https://wordpress-1301098-5039235.cloudwaysapps.com/testing-the-live-reg-webinar/ — TY-Beispiel: https://wordpress-1301098-5039235.cloudwaysapps.com/testing-the-live-reg-webinar/?confirmed&live=&lid=e52670c95b494350f944a9cc7f69a69085967d5a&code=
- **Live HC (Reg + TY, id=8):**  
  Reg: https://wordpress-1301098-5039235.cloudwaysapps.com/2025-11-15-tobias/ — TY-Beispiel: https://wordpress-1301098-5039235.cloudwaysapps.com/2025-11-15-tobias/?confirmed&lid=6f865f732dac1964423c7e8ffabd97fb09da0c38&code=
- **Legacy-CP / Bootstrap-LP (älteres Beispiel-Slug, ggf. ohne gültige Kampagne):**  
  https://wordpress-1301098-5039235.cloudwaysapps.com/registration-1-for-webinar-3/
- **Gutenberg / FSE (älteres Beispiel-Slug):**  
  https://wordpress-1301098-5039235.cloudwaysapps.com/123-register-now-3/
- **Dankeseite (älterer Fallback, `lid` kann veraltet sein):**  
  https://wordpress-1301098-5039235.cloudwaysapps.com/2025-11-14/?confirmed&lid=dfb914dc21235f0e47e432a62ad39e3b0b45a396&code=&webinar=
- **Registrierung (älteres Auto-Register-Beispiel):**  
  https://wordpress-1301098-5039235.cloudwaysapps.com/new-auto-register-now-3/
- **TY-Test bevorzugt frisch:** Wenn Registrierung moeglich ist, erst auf der passenden Registration-URL anmelden und dann die dadurch erzeugte Thank-you-URL mit frischer `lid` fuer Screenshot/DOM-Pruefung nutzen. Alte Fallback-`lid`s koennen 404 oder veraltete Daten liefern.
- **Live-Webinar (HC kanonisch, id=8):**  
  https://wordpress-1301098-5039235.cloudwaysapps.com/2025-11-15-tobias/?live=1&webinar=1&lid=6f865f732dac1964423c7e8ffabd97fb09da0c38&watch_type=live
- **Live-Webinar (älteres Beispiel mit `?live&webinar&lid`, ggf. ungültige `lid`):**  
  https://wordpress-1301098-5039235.cloudwaysapps.com/123-2/?live&webinar&lid=89e1c0134b76386667d74c61ea56a32583073aa2&watch_type=live
- **Freemius Opt-in Copy — Preview (Staging, wp-admin Login, `manage_options`):** Code: `webinarignition.php` · `wi_optin_preview=fresh|update` (nur Admin-Preview, kein Prod-State). **Extern öffnen.**
  - **Update / Upgrade:** https://wordpress-1301098-5039235.cloudwaysapps.com/wp-admin/admin.php?page=webinarignition-dashboard&require_license=false&wi_optin_preview=update
  - **Fresh install:** https://wordpress-1301098-5039235.cloudwaysapps.com/wp-admin/admin.php?page=webinarignition-dashboard&require_license=false&wi_optin_preview=fresh

### Danke-Seite (TY) auf Block-Theme — Body-Klasse prüfen

- TY kann **`body.wi-wb-registration-landing`** nutzen (Template `page-webinar-registration`) **ohne** `wi-ty-gb-page` — dann greifen Regeln in **`ty-gutenberg.css` / `cpres_ty.css`** nicht.
- **Selbstprüfung:** `curl` der TY-URL → `<body class="…">` lesen; bei `wi-wb-registration-landing` TY-CSS in **`webinar-modern.css`** (Scope `.ticketCDArea` + `:has(.wi-ty-countdown-panel)`), nicht nur Gutenberg-TY-Styles.
- **Spezifität:** `body… .ticketCDArea a.ticketCDAreaBTN { display:flex !important }` kann schwächere `a.wi-ty-live-join-hidden { display:none }` überschreiben — versteckten Live-Link mit **`body.wi-wb-registration-landing .ticketCDArea a.wi-ty-live-join-btn.wi-ty-live-join-hidden`** absichern.

### Referenz — Registrierung / FSE-Template

Bei Tasks zu **Registrierungs-Landing**, **„Webinar Registration“-Template**, **Footer-Shortcode** oder **Frontend vs. Editor:**

1. Screenshot(s) der geprüften **Live-URL(s)** (unter `ss/`, z. B. `ss/ss-verify-….png`).
2. Link/Abgleich mit **Edit Page** und Template **Webinar Registration** im Editor.

Referenzbild (Editor, Template aktiv), Beispiel-Pfad (Dateiname kann variieren):  
[`file:///Users/tobiasconrad/.cursor/projects/Users-tobiasconrad-Documents-webinarignition-bitbucket/assets/Edit-Page-_123-Register-Now-3_-_-wordpress-1301098-5039235-cloudwaysapps-com-_-WordPress-2026-04-11_12_16-584eb966-065a-418a-9b0d-9e33bc3ef03d.png`](file:///Users/tobiasconrad/.cursor/projects/Users-tobiasconrad-Documents-webinarignition-bitbucket/assets/Edit-Page-_123-Register-Now-3_-_-wordpress-1301098-5039235-cloudwaysapps-com-_-WordPress-2026-04-11_12_16-584eb966-065a-418a-9b0d-9e33bc3ef03d.png)

**Abgleich:** Frontend soll **WI-Layout** inkl. `[webinarignition_footer]` nutzen, nicht Standard-`page` mit vollem Theme-Footer.

## Schritt 7 — Git (nur nach User-Bestätigung)

```bash
cd ~/Documents/webinarignition-bitbucket
git pull origin main
git add GEAENDERTE_DATEIEN
git commit -m "fix: kurze beschreibung was gefixt wurde"
git push origin main
```

Vor jedem Push: **`git pull origin main`** (Team, z. B. Talha).

---

<!-- source: .cursor/rules/wi-deliverable-loop.mdc -->

# Lieferpfad Cloudways — Kurz & verbindlich

Gilt für **jeden** Task, der ein **sichtbares Ergebnis auf Staging** hat — explizit u. a. **Webinar-Listen** (`UI/webinars-list.php` und zugehörige Admin-/Listen-UI), Registrierung, Grid, TY. Ausführliche Stufen, `#A###`, Chat-Format: **`wi-agent-workflow.mdc`**. Befehle, Pfade, Screenshot-Befehle: **`wi-staging-infra.mdc`**.

## Bekannte Site-URL — immer mitliefern (besonders wenn du wartest)

- Sobald die **Ziel-Site oder Prüf-URL** feststeht (aus User-Nachricht, Task, **`wi-staging-infra.mdc`** oder Kontext): **notieren** und nach **Deploy + Cache** in der Antwort **vollständig als https-Link** wiederholen — nicht nur „deployed“ oder „bitte Staging öffnen“.
- **Testing / User wartet auf Test:** explizit dieselbe(n) **Test-URL(s)** nennen (Webinar-Preview, Settings, Grid, …), optional **Zeit / Version** kurz (`WEBINARIGNITION_VERSION`), damit ohne Suche dieselbe Seite geöffnet werden kann.
- Weicht die URL vom Default-Staging ab: **immer die genannte URL** für Selbsttest und Chat-Abschluss verwenden.

## Prüf-URLs öffnen (Tobias 2026-07-05 — Cursor-Browser Default)

**Immer** passend öffnen — nicht nur im Chat verlinken. **Kein** macOS `open "https://…"` und **kein** headed System-Chrome tagsüber (07:00–23:00) — stört paralleles Arbeiten.

| URL-Typ | Beispiel | Öffnen (Pflicht) |
|-----|----|-----|
| **Login nötig** | Jede URL mit **`wp-admin`**, Plugin-UI, WI-Dashboard | **Cursor-Browser** (`browser_navigate` / MCP) — einmal einloggen, Session wiederverwenden. **Nicht** `open` / neues Chrome-Fenster. |
| **Ohne Login** | Reg, TY, Grid, Live, Replay (öffentlich) | **Cursor-Browser** — `browser_navigate`. |

**Headed Google Chrome / `wi-visible-target-screenshot.sh` ohne `--headless`:** nur **Notfall** (Cursor-Browser zeigt Ziel nicht) **oder** nachts **23:00–05:00** wenn Laptop an.

- Im Chat: nur **Cursor-Browser**-Links für Admin + TN (kein Abschnitt „Extern öffnen“ mit `open`).
- Screenshots: `.cursor-wi-screenshots/ss-….png` — **Default QA:** unbegrenzt `npm run qa:smart:matrix` (L0–L4 kostenlos). PNG/Vision (L5–L7) nur bei Fail oder wenn nicht anders moeglich.
- **WI General Settings (kanonisch):** `admin.php?page=webinarignition_settings&tab=general` — **nicht** `webinarignition-settings` (Bindestrich). Code: `wi_get_admin_general_settings_url()`. Siehe `wi-staging-infra.mdc`.
## Chat-Links — wp-admin im Cursor-Browser (Tobias 2026-07-05)

| URL-Typ | Öffnen | Warum |
|---|---|---|
| **`wp-admin`** + öffentliche TN-URLs | **Cursor-Browser** — `browser_navigate` + Markdown-Link im Chat | Eine Session, kein extra Chrome-Fenster |
| **Headed Chrome / `open "url"`** | Nur Notfall oder **23:00–05:00** | Tagsüber parallele Arbeit nicht stören |

**Pflicht im Chat:**
- **Klickbarer Markdown-Link** zur Prüf-URL — alles über Cursor-Browser.
- **Nicht** `open "url"` im Terminal vor der Nachricht.
- **Nicht** headed Chrome für Admin-QA tagsüber, wenn Cursor-Browser den Zielzustand zeigt.

**Legacy (nur wenn Cursor-Browser Admin-Ziel nicht zeigt):**
- `./scripts/fix-cursor-external-links.sh` bei Bedarf; headed Chrome als Escalation siehe **`wi-screenshot-hygiene.mdc`**.

## Reihenfolge (nicht überspringen)

1. **Deploy:** Bei jedem Staging-sichtbaren Change zuerst `webinarignition.php` **Plugin-Header `Version:` + `WEBINARIGNITION_VERSION`** gemeinsam bumpen, `webinarignition.php` **immer mit** den geänderten Dateien nach `public_html/wp-content/plugins/webinarignition-premium/` deployen (nie `/home/…` als SCP-Ziel), Upload-Version verifizieren, danach **`wp cache flush`** per SSH.
2. **Selbsttest:** Task-relevante **Staging-URL** prüfen — **wenn wp-admin/Plugin-UI:** zuerst **Credentials** aus `STAGING-ACCESS.local.md` (**finden → auslesen → einloggen**), dann Screenshot der **konkreten** Admin-Prüf-URL; sonst anonym headless wie in **`wi-staging-infra.mdc`**. Ausgabe: **`.cursor-wi-screenshots/ss-….png`**. PNG **selbst** prüfen: Der relevante Zielzustand muss **sichtbar und eindeutig belegbar** sein. **Wenn Headless/Cursor das Ziel nicht zeigt:** sofort **`wi-screenshot-hygiene.mdc` Escalation (4 Schritte)** + `./scripts/wi-visible-target-screenshot.sh` — kein Abschluss mit irrelevantem PNG. Wenn nicht (falscher Tab, falscher Scrollstand, relevante Stelle außerhalb des Bildes, nur alte Chat-Meldungen statt neuester Zustand), aktiv nachjustieren: scrollen, klicken, warten, Viewport ändern oder eine echte eingeloggte Browser-/Playwright-Session nutzen und Screenshot neu aufnehmen. **Kein Abschluss mit Screenshot, der die Behauptung nicht zeigt.** **Nach Selbsttest:** dieselbe **https-Prüf-URL** in der Nachricht ausgeben (siehe Abschnitt oben).
3. **Loop:** Wenn der Screenshot nicht passt → zurück zum Fix → erneut Deploy, Cache, Screenshot.
4. **Erst wenn gut — „fertig“ im Chat:** Abschließende Antwort **mit** vollständiger **https://…**-Prüf-URL **und** Screenshot(s): eingebettet (`![…](ss/…)` **plus** klickbarer Markdown-Link zur gleichen PNG. **Keine** Task-Zusammenfassung als erledigt und **kein** „ist live“ **vor** Schritten 1–2 (außer nachweislich kein Netzwerk/SSH — dann dokumentieren).

**Git:** Kein Commit/Push ohne User-**`working ✓`** bzw. **`#Axxx working`** (`wi-agent-workflow.mdc`).

---

<!-- 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).

---

<!-- source: .cursor/rules/wi-site-file-change-notes.mdc -->

# WI — Site File Change Notes

When a task touches a specific external/staging/customer site, record the working file-change path for that site once it is known.

Default preference: the agent uploads and self-tests changes autonomously. Do not default to asking Tobias to upload a ZIP.

If Tobias says a specific staging/test host is broken, asks for a smoke across the approved test matrix, or names a target like Engine X, Microsoft/IIS, Apache, LightNode OpenLiteSpeed, webinarignition.com staging, or Cloudways Staging, that is explicit permission for the agent to write plugin files to that target when needed to fix or verify the issue. The permission is target-scoped and task-scoped: use only the documented local credentials/upload path for that site, upload only plugin ship-set files, and self-test immediately.

The agent should not rediscover access from scratch every time. First check existing rules, local site notes, known env filenames, and successful prior commands; only probe a new upload path when no working path is documented or the documented path fails. After the first successful upload to a target, update the non-secret note so the next agent can reuse it.

For each site, note at minimum:

- Site/base URL and label.
- Small-change upload path preferred for the agent: WP Admin browser, Application Password / REST, helper plugin, Code Snippets, FTP/SFTP, or other.
- Which local env file is used.
- Which script or command changes files there.
- Remote plugin root/path if applicable.
- File list used for the successful upload.
- Verification method and known blockers.

Test available upload methods once when setting up a site, then use the working path in later chats. If a method stops working, retest and update the note instead of rediscovering everything from scratch.

Escalation: ask Tobias for manual full ZIP upload only when autonomous paths are unavailable, blocked, or the site is far behind and a full plugin replacement is safer than small file writes.

Do not write secrets into notes, chat, commits, or rules. Store only non-secret variable names, paths, commands, and outcomes.

## Client Sites With WP-Admin Only

When fixing customer/client sites where SSH/FTP/SFTP is unavailable but WP Admin access exists:

- Prefer autonomous agent delivery: build a scoped plugin ZIP, log into wp-admin with the local access note, upload via Plugins > Add Plugin > Upload Plugin, click `Replace current with uploaded`, then self-test the affected public/admin URL.
- For small fixes, do not ask Tobias to upload unless browser automation, WP credentials, App Password, helper plugin, or wp-admin upload is blocked.
- If an Application Password, Code Snippets runner, or helper plugin upload endpoint works, prefer the smallest safe upload path and note it for that site.
- If a full ZIP replacement is used, include the version bump in `webinarignition.php` and verify the public page/source serves that version.
- After the first successful client-site upload, update the site note with: login/upload path, ZIP build command, replace step, verification URL, screenshot path, and blockers.
- Ask Tobias for manual upload only when the site is far behind, the change spans many/new files and automation is blocked, or a human approval point is needed.

---

<!-- source: .cursor/rules/wi-autonomous-multiserver-upload-test.mdc -->

# WI — Autonomous Multiserver Upload + Retest

When a multiserver target is required for QA and SFTP/FTP/SSH deploy is blocked, the agent must not stop at "please upload manually" if wp-admin access exists.

For staging/test targets, the normal agent loop is **fix locally -> upload to the named target -> verify there -> screenshot/log result**. "Staging is broken" or "run smoke across the test sites" is enough authorization to deploy to those staging/test targets when the deploy is necessary for diagnosis or verification. This does not authorize production/customer writes outside the current task scope.

Autonomous fallback order:

1. Try the normal deploy path: SFTP/FTP/SSH/lftp, structure-preserving, with version verification.
2. If deploy fails or hangs, mark that transport as unreliable, but continue with wp-admin when credentials exist.
3. Log in via the target's normal wp-admin URL or documented default-form login URL.
4. Open `wp-admin/plugin-install.php?tab=upload` or the Plugins upload screen.
5. Build/use the current plugin ZIP locally, excluding local/dev files and secrets.
6. Upload via wp-admin, replace/update the existing plugin when WordPress asks, and keep the plugin active.
7. Verify the target version from public asset URLs or wp-admin plugin details.
8. Run the same public smoke matrix immediately: GB Evergreen prefill, GB Live prefill, fresh One-Click Live, TY, Calendar/ICS, Live/Replay where valid, plus **mail smoke** (Version + Confirmation + Cron-Reminder Slot 1/2) per [wi-multiserver-mail-smoke.mdc](wi-multiserver-mail-smoke.mdc).
9. Save screenshots under `.cursor-wi-screenshots/` and record pass/fail in the active plan/runbook.
10. Only ask Tobias when wp-admin login, upload UI, a destructive confirmation, or host-level permission blocks the flow.

Never use password-manager CLI. Use local credential files only. Do not commit upload ZIPs, screenshots, or local access notes.

---

<!-- source: .cursor/rules/wi-local-credentials-only.mdc -->

# Lokale Credentials Only

Dieses Repo nutzt ab jetzt **keine password-manager-/CLI-Workflows** mehr.

## Verbindlich

### 1Password CLI und Integration (hart)

- **Keine** Aufrufe der **1Password CLI** (`op`, z. B. `op read`, `op inject`, `op signin`, `op run`) in Repo-Skripten, Hooks, Cursor-Rules oder Agent-Workflows.
- **Keine** Secret-Reference-URLs (`op://…`) und keine Anleitung, die auf 1Password-CLI oder Browser-Extension als Quelle für Secrets setzt.
- Wenn Shell/SSH/Deploy einen **1Password- oder Passwort-Manager-Prompt** öffnet: **abbrechen** — Zugang nur über **lokale Dateien** (siehe „Lokale Quellen“) oder bereits gesetzte Umgebungsvariablen.

- Keine password-manager CLI-Befehle (insb. **1Password `op`**), Secret-Reference-URLs (`op://…`) oder password-manager Browser-/Cursor-Extension verwenden.
- Keine password-manager SSH-agent-Pfade- oder SSH-agent-socket-Konfiguration empfehlen.
- Secrets kommen ausschließlich aus lokalen, gitignorierten Dateien oder aus bereits gesetzten Shell-Umgebungsvariablen.
- Lokale Secret-Dateien nie committen und keine Passwörter/API-Keys im Chat ausgeben.

## Lokale Quellen

- Staging SFTP: `.env.cloudways-staging.local` aus `.env.cloudways-staging.local.example`.
- WP-Admin Playwright: `scripts/env/staging-wp-browser.local.env` aus `scripts/env/staging-wp-browser.local.env.example`.
- REST/Relay Probe: `scripts/env/staging-relay-probe.local.env` aus `scripts/env/staging-relay-probe.local.env.example`.
- Freemius Upload: `scripts/env/freemius-upload.local.env` aus `scripts/env/freemius-upload.local.env.example`.

## SSH/Git

Wenn SSH einen password-manager key prompt zeigt, nicht autorisieren und nicht wiederholen. Stattdessen lokale SSH-Keys in `~/.ssh/config`/`ssh-add` oder Passwort-SFTP/lftp verwenden.

