Files
WP-LIT-MCP/docs/superpowers/specs/2026-09-29-divi-absicherung-design.md

211 lines
17 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Divi-Absicherung – Design-Spezifikation
- **Datum:** 2026-09-29
- **Status:** Entwurf zur Freigabe
- **Autor:** Jan-Niclas Loosen (Loosen-IT), erarbeitet mit Claude
- **Einordnung:** eigene Etappe vor Etappe 4 (Designsystem schreiben); Etappe 4 baut darauf auf.
## 1. Ziel und Kontext
### 1.1 Problem
Die Divi-Integration hängt an Divi-Interna, nicht an einer zugesicherten Schnittstelle (Divi 5 bietet zum Schreiben keine an). Stand 0.5.0:
- 19 Dateien rufen Divi direkt auf (`et_*`-Funktionen, `ET\…`-Klassen, `_et_*`-Meta, Option `et_divi`), auch außerhalb von `Integrations/Divi/` (`MediaUsageFinder`, `PostPresenter`, `ContentSchema`).
- Einige Stellen **bilden Divi-Speicherlogik nach** (Theme-Builder-Templateliste/Bedingungen/`_et_enabled` nach `et_theme_builder_api_save()`, Bibliothekseinträge nach `et_pb_submit_layout()`, Builder-Kennzeichen nach `SyncToServerController`). Ändert Divi diese Logik, schreibt das Plugin still falsche Daten.
- Welche Divi-Teile genutzt werden, steht an drei Orten (`DiviCompatibility`, `DiviThemeBuilder::FUNCTIONS`, Docblocks mit Zeilennummern aus Divi 5.2.1). `DiviCompatibility` prüft nur Existenz, nicht Verhalten.
- Eine Divi-Version wird nicht geprüft.
### 1.2 Ziele (Vorgabe Auftraggeber)
1. **Weniger Aufwand pro Divi-Update.**
2. **Ungetestete Divi-Version → nichts schreiben, klare Antwort.** Lesende Divi-Tools laufen weiter mit Hinweis „Achtung, ungetestet“; schreibende Divi-Tools sind gesperrt und sagen warum.
3. **Freigabe ohne Plugin-Update möglich:** getestete Versionen stehen fest im Code; zusätzlich kann ein Administrator auf einer Website im WP-Admin eine Prüfung laufen lassen und die Version danach freigeben (Eintrag in der Datenbank). Freigeben geht nur nach bestandener Prüfung.
4. **Wartbarkeit:** Divi-Zugriffe an einer Stelle bündeln (bestehender Code wird umgebaut, nicht nur neuer Code).
### 1.3 Erfolgskriterien
1. Außerhalb der fünf Divi-Adapter (§3) gibt es keinen direkten Divi-Zugriff; ein Unit-Test erzwingt das.
2. Jede genutzte Divi-Funktion/-Klasse/-Methode, jeder Post-Typ, jede Taxonomie, jeder Meta-Schlüssel und jede Option steht im Manifest ihres Adapters; nachgebaute Divi-Funktionen haben einen Fingerabdruck.
3. Mit einer nicht freigegebenen Divi-Version schreibt kein Divi-Tool; die Antwort trägt `divi_untested` bzw. `divi_incompatible` und einen deutschen Hinweis. Lesende Divi-Tools liefern ihr Ergebnis mit Hinweis in `warnings`.
4. Die Admin-Prüfung läuft auf einer Live-Website ohne sichtbare Spuren: die Wegwerfseite ist danach in jedem Fall gelöscht.
5. Alle bestehenden Unit-Tests und Smoke-Skripte sind nach dem Umbau unverändert grün (Umbau ohne Verhaltensänderung).
6. Eine neue Divi-Version lokal prüfen ist ein Befehl (`wp lit-mcp divi-check`); bei Rot zeigt `--diff` die geänderten Divi-Funktionen.
### 1.4 Nicht Teil dieser Etappe
- Sperren für Pods (nutzt nur die öffentliche Pods-API) oder den MCP-Adapter.
- Automatisierter Browsertest des Visual Builders.
- Schreiben ins Designsystem (Etappe 4 – dort gleich mit Manifest).
## 2. Begriffe
- **Adapter:** Klasse, die als einzige Divi direkt anspricht (§3).
- **Manifest:** Beschreibung der Divi-Abhängigkeiten eines Adapters (§4).
- **Nachbau (Mirror):** Plugin-Code, der Speicherlogik einer Divi-Funktion wiederholt, statt sie aufzurufen.
- **Fingerabdruck:** SHA-256 des normalisierten Quelltexts einer Divi-Funktion/-Methode (§4.2).
- **Live-Prüfung:** Abgleich aller Manifeste mit dem installierten Divi (Existenz + Fingerabdrücke), zur Laufzeit, zwischengespeichert (§5.4).
- **Admin-Prüfung:** vollständige Prüfung aus dem WP-Admin inkl. Schreibtest (§6).
## 3. Divi-Grenze
Nur fünf Adapter greifen auf Divi zu. Vorhandene Klassen werden wo möglich zu Adaptern bzw. in sie hineingezogen, damit der Umbau klein bleibt.
| Adapter | Übernimmt (Stand 0.5.0) |
|---|---|
| **Seiten** | `DiviSaver` (Divi-Teil), `DiviFlags`, CSS-Cache `ET_Core_PageResource`, `WpLinkTargets` (Divi-Teil) |
| **Bibliothek** | `DiviLibraryStore`, `LibraryItemSpec`, `NewLibraryEntry`, `DiviRoles`, `LibraryAccess` (Divi-Teil), `GlobalLayout` |
| **Theme Builder** | `DiviThemeBuilder`, `Template`, `TemplateConditions` (Divi-Teile) |
| **Designsystem** | `DesignSystemReader`, `DesignSystem` (Divi-Teil), Option `et_divi` inkl. `divi_logo` für `MediaUsageFinder` |
| **Katalog** | `DiviModules`, `DynamicContentMap`, Divi-Post-Typen/Flags für `PostPresenter` und `ContentSchema` |
Die genaue Klassenaufteilung legt der Plan fest; Regel: jede Divi-Berührung liegt in genau einem Adapter, der Rest des Plugins nutzt nur dessen Methoden.
Parser, Serializer und `Compose/` bleiben, wo sie sind: sie kennen das Divi-**Format** (Blockmarkup, Attributpfade), rufen aber keine Divi-**Funktionen** auf. Ihr Schutz ist die Katalog- und Formatprüfung (§6.2 Schritte 2–3).
**Architekturtest (Unit):** durchsucht `src/`; außerhalb der Adapter-Dateien sind `et_*(`-Aufrufe, `ET\…`- und `ET_…`-Klassen sowie `'_et_…'`/`'et_divi'`-Schlüssel ein Fehler. Innerhalb der Adapter ist jede solche Fundstelle ein Fehler, die nicht im Manifest des Adapters steht. Während des Umbaus gibt es eine Ausnahmeliste für Altbestand; am Ende der Etappe ist sie leer und wird entfernt.
## 4. Manifest
### 4.1 Inhalt
Jeder Adapter liefert ein Manifest (festes PHP-Wertobjekt, keine Konfigurationsdatei) mit:
- `functions`: Divi-Funktionen, die aufgerufen werden;
- `methods`: Klasse + Methode (+ erwartete Mindest-Parameterzahl), z. B. `ET\Builder\Packages\GlobalData\GlobalData::get_global_colors`;
- `classes`: Klassen, deren Existenz genügt;
- `post_types`, `taxonomies`, `block_types`;
- `meta_keys`, `options`: Divi-Schlüssel, die gelesen oder geschrieben werden (dokumentarisch; Architekturtest nutzt sie);
- `mirrors`: nachgebaute Divi-Funktionen/-Methoden mit Fingerabdruck und Kurzbeschreibung, was nachgebaut wird;
- `sources`: Quellenangaben (Divi-Datei, Funktion) – ersetzt die Zeilennummern in Docblocks. Zeilennummern entfallen.
### 4.2 Fingerabdruck
`sha256` über den Quelltext der Funktion bzw. Methode (per `ReflectionFunction`/`ReflectionMethod`: Datei, Start- und Endzeile), normalisiert mit `token_get_all()`: `T_COMMENT`, `T_DOC_COMMENT`, `T_WHITESPACE` entfernt, übrige Tokens aneinandergehängt. Kommentar- und Formatänderungen ändern den Fingerabdruck nicht; Codeänderungen schon.
Die Soll-Fingerabdrücke werden mit dem Plugin ausgeliefert (erzeugte Datei im Plugin, z. B. JSON neben den Manifesten). Die normalisierten Soll-Quelltexte liegen nur für die Entwicklung unter `tests/Fixtures/divi/reference/` (per `.gitattributes` nicht im Plugin-Paket) und dienen `--diff` (§7).
### 4.3 `DiviCompatibility`
Verliert ihre handgepflegte Liste und prüft alle Manifeste: Existenz von Funktionen/Klassen/Methoden (inkl. Parameterzahl), Post-Typen, Taxonomien, Block-Typen; Fingerabdrücke der Mirrors. Ergebnis: Liste roter Punkte mit Adapter, Divi-Symbol, Fundort in der installierten Version. Die bisherigen Sonderprüfungen (z. B. `divi/global-layout` registriert oder in den Core-Metadaten) werden Manifest-Einträge mit eigener Prüfregel.
## 5. Freigabe und Sperre zur Laufzeit
### 5.1 Divi-Version
Aus dem aktiven Theme wie bisher (`EnvironmentProbe`: Template `Divi`, bei Child-Theme Version des Eltern-Themes).
### 5.2 Zustände
| Zustand | Bedingung | Schreibende Divi-Tools | Lesende Divi-Tools |
|---|---|---|---|
| `released` | Version freigegeben (§5.3) **und** Live-Prüfung grün | laufen | laufen |
| `untested` | Live-Prüfung grün, Version nicht freigegeben | gesperrt, `divi_untested` | laufen, Hinweis |
| `incompatible` | Live-Prüfung rot | gesperrt, `divi_incompatible` | laufen, Hinweis |
Die Live-Prüfung zählt immer, auch für Versionen aus der Code-Liste (erkennt z. B. Hotfixes mit gleicher Versionsnummer). Ist Divi nicht aktiv, gibt es wie bisher keine Divi-Tools; der Zustand ist dann ohne Bedeutung.
### 5.3 Quellen der Freigabe
Eine Divi-Version ist freigegeben, wenn sie
1. **im Code** steht: feste Liste getesteter Versionen (PHP-Konstante), vom Entwickler gepflegt; **oder**
2. **in der Datenbank der Website** steht: Option `lit_mcp_divi_releases`, Liste von Einträgen `{divi_version, plugin_version, user_id, released_at, report_id}`. Ein Eintrag entsteht nur über die Admin-Prüfung nach grünem Bericht (§6.3) und gilt **nur für die Plugin-Version, mit der geprüft wurde**. Nach einem Plugin-Update muss eine nur per Datenbank freigegebene Divi-Version neu geprüft werden, außer sie steht inzwischen in der Code-Liste. Ein Administrator kann Einträge zurückziehen.
Multisite: pro Website (Optionen der jeweiligen Website).
### 5.4 Live-Prüfung und Zwischenspeicher
Die Live-Prüfung (Manifeste, §4.3) wird im Transient `lit_mcp_divi_state` zwischengespeichert, Schlüssel aus Divi-Version, Plugin-Version und `filemtime` der Dateien aller Mirrors und Methoden. Weicht der Schlüssel ab oder lief ein Theme-Update (`upgrader_process_complete` für das Divi-Theme), wird neu berechnet. Kosten ohne Cache: Reflection + Tokenisieren von rund zehn Funktionen.
### 5.5 Sperre
**Zentral (`AbilityRegistrar`):** Für jedes Tool der Divi-Integration wird vor `execute` der Zustand ermittelt.
- Schreibendes Tool (Annotation nicht `readonly`) und Zustand ≠ `released`: sofort `WP_Error` mit Code `divi_untested` bzw. `divi_incompatible`, keine Ausführung, Protokolleintrag als abgelehnter Aufruf.
- Lesendes Tool und Zustand ≠ `released`: normale Ausführung, Hinweis in `warnings` des Ergebnisses.
Welche Tools „Divi-Tools“ sind, ergibt sich aus der Integrations-Zugehörigkeit (`IntegrationRegistry::groupMap()`); Divi-lastige Tools anderer Integrationen deklarieren sich zusätzlich (Ability-Eigenschaft „nutzt Divi“), ein Unit-Test prüft die Zuordnung.
**In den Adaptern:** Jede schreibende Adapter-Methode prüft den Zustand selbst und wirft bei ≠ `released` eine eigene Ausnahme, die auf dieselben Fehlercodes abgebildet wird; bestehendes Rollback greift. Das deckt Wege ab, die Divi-Interna indirekt nutzen (z. B. Content-Update einer Divi-Seite über `DiviSaver`). Generisches Kopieren/Wiederherstellen von Posts (`PostCloner`, Revisionen) berührt keine Divi-Interna und bleibt ungesperrt.
**Prüfmodus:** Der Schreibtest der Admin-Prüfung (§6.2 Schritt 4) muss trotz `untested` schreiben. Er setzt dafür eine request-lokale Ausnahme, die ausschließlich für die ID der Wegwerfseite gilt; sie ist nicht von außen setzbar (kein Parameter, keine Option).
### 5.6 Hinweistexte
- `divi_untested`: „Achtung: Divi {version} ist auf dieser Website noch nicht für LIT MCP freigegeben. Schreibende Divi-Werkzeuge sind gesperrt, bis ein Administrator unter Einstellungen → LIT MCP → Divi die Prüfung ausführt und die Version freigibt. Es wurde nichts geändert.“
- `divi_incompatible`: „Divi {version} hat Stellen geändert, die LIT MCP nutzt. Schreibende Divi-Werkzeuge sind gesperrt, bis LIT MCP aktualisiert wurde. Es wurde nichts geändert.“
- Lesehinweis (`warnings`): „Achtung: Divi {version} ist für LIT MCP ungetestet (bzw. inkompatibel); Lesen funktioniert, Schreiben ist gesperrt.“
### 5.7 `lit-site-status`
Zeigt für Divi zusätzlich: Zustand, Quelle der Freigabe (`code`/`database` mit Datum und Benutzer), rote Punkte der Live-Prüfung.
## 6. Admin-Prüfung
### 6.1 Oberfläche und Rechte
Neuer Reiter **„Divi“** unter Einstellungen → LIT MCP: Zustand, Divi-Version, Quelle der Freigabe, letzter Prüfbericht, Knöpfe „Prüfung starten“, „Divi {version} freigeben“, „Freigabe zurückziehen“. Alle Aktionen: `manage_options`, Nonce, `admin-post`-Aktionen wie die übrigen Reiter (`AdminPostAction`). Eine Sperre (Transient) verhindert zwei gleichzeitige Prüfungen.
### 6.2 Prüfschritte
1. **Manifeste:** Live-Prüfung (§4.3), ohne Cache.
2. **Modulkatalog:** Alle Attributpfade aus `ShortformMap` existieren im Live-Katalog von Divi. Die Logik von `tests/Integration/divi-shortform-check.php` wandert dafür ins Plugin; das Skript nutzt danach die Plugin-Klasse.
3. **Format im Speicher:** Ein mit dem Plugin ausgelieferter Referenzinhalt (Divi-5-Blockmarkup, deckt die Alltagsmodule ab) wird mit dem Plugin-Parser gelesen und serialisiert und muss byte-identisch herauskommen; `parse_blocks()` muss dieselbe Blockstruktur (Namen, Verschachtelung) liefern.
4. **Schreibtest auf Wegwerfseite:** privater Entwurf „[LIT MCP Prüfung] {Datum}“ (Post-Typ `page`); über den normalen Schreibweg (Composer → Serializer → Seiten-Adapter) je ein Exemplar der Alltagsmodule schreiben; prüfen: Inhalt gespeichert wie serialisiert, Builder-Kennzeichen gesetzt, Rendern über Divi ohne PHP-Fehler/Warnung und mit den erwarteten Texten in der Ausgabe. Kein Eintrag im Änderungsprotokoll. Im `finally` immer `wp_delete_post($id, true)` (endgültig, mit Revisionen und Meta); schlägt das fehl, nennt der Bericht die ID zum Löschen von Hand und der Bericht ist rot.
Jeder Schritt meldet grün/rot mit Einzelpunkten. Ein Fehler in einem Schritt bricht die übrigen nicht ab (außer Schritt 4 bei rotem Schritt 1: dann kein Schreibtest).
### 6.3 Bericht und Freigabe
Der Bericht wird in der Option `lit_mcp_divi_check_report` gespeichert (letzter Bericht; ID, Zeit, Benutzer, Divi-Version, Plugin-Version, Cache-Schlüssel aus §5.4, Ergebnisse). „Freigeben“ ist nur aktiv – und wird serverseitig nur ausgeführt –, wenn der letzte Bericht vollständig grün ist und Divi-Version, Plugin-Version und Cache-Schlüssel zum aktuellen Stand passen. Freigabe und Zurückziehen werden im Änderungsprotokoll vermerkt.
Hinweis im Bericht (blockiert nichts): „Optional: eine Divi-Seite im Visual Builder öffnen und speichern.“
## 7. WP-CLI für die Entwicklung
`wp lit-mcp divi-check` (nur registriert, wenn `WP_CLI` definiert ist) führt die Prüfschritte aus §6.2 aus und gibt den Bericht aus; Rückgabecode ≠ 0 bei Rot. Freigaben schreibt der Befehl nicht.
- `--diff`: zeigt für jeden roten Mirror den Unterschied zwischen Soll-Quelltext (`tests/Fixtures/divi/reference/`) und installiertem Quelltext, mit Datei und Zeilen in der installierten Version.
- `--update-reference`: schreibt Soll-Fingerabdrücke und Referenz-Quelltexte neu – nur, wenn alle übrigen Prüfschritte grün sind. Gedacht nach dem Anpassen eines Nachbaus.
Ablauf bei neuer Divi-Version: lokal einspielen → `wp lit-mcp divi-check` → bei Grün Version in die Code-Liste eintragen; bei Rot `--diff`, Nachbau anpassen, `--update-reference`, Smoke-Skripte, Version eintragen → Plugin-Release.
## 8. Daten und Deinstallation
Neu: Optionen `lit_mcp_divi_releases`, `lit_mcp_divi_check_report`; Transients `lit_mcp_divi_state`, Prüfsperre. `Uninstaller` und `uninstall.php`-Doku werden ergänzt.
## 9. Fehlerbehandlung
- Reflection/Tokenisieren scheitert (Funktion fehlt, Datei nicht lesbar): Punkt rot → `incompatible`, nie Ausnahme nach außen.
- Zustand nicht ermittelbar (unerwartete Ausnahme): wie `incompatible` behandeln (Schreiben gesperrt), Protokoll-Fehlereintrag.
- Admin-Prüfung bricht ab (Timeout, Fatal im Rendern): Wegwerfseite im `finally` gelöscht; bei Fatal zusätzlich beim nächsten Aufruf des Reiters nach verwaisten „[LIT MCP Prüfung]“-Entwürfen des Plugins suchen (Marker-Meta) und löschen.
## 10. Tests
**Unit:**
- Zustandsberechnung: alle Kombinationen aus Code-Liste, DB-Eintrag (passende/andere Plugin-Version), Live-Prüfung grün/rot.
- Sperre in `AbilityRegistrar`: lesend/schreibend × drei Zustände; Fehlercodes und Texte; `warnings` bei lesenden Tools.
- Sperre in Adaptern inkl. Prüfmodus nur für die Wegwerfseiten-ID.
- Fingerabdruck: Kommentar/Leerraum ändert nichts, Codeänderung schon.
- Architekturtest (§3) und Test „jedes Divi-Tool ist als solches erkannt“.
- Freigabe nur bei frischem grünem Bericht (Version, Plugin-Version, Cache-Schlüssel); Rechte und Nonce.
- `Uninstaller` entfernt die neuen Optionen/Transients.
**Integration gegen echtes Divi (Smoke-Skript):**
- Admin-Prüfung komplett grün; Wegwerfseite danach nicht mehr vorhanden (auch keine Revisionen).
- Künstlich veränderter Soll-Fingerabdruck → `incompatible`, schreibendes Tool liefert `divi_incompatible`, lesendes Tool liefert Ergebnis + Hinweis.
- Version aus Code-Liste entfernt → `untested`; nach Admin-Prüfung + Freigabe → `released`.
- `wp lit-mcp divi-check` grün; `--diff` bei verändertem Soll zeigt den Unterschied.
**Regression:** alle bestehenden Unit-Tests und Smoke-Skripte unverändert grün.
## 11. Umsetzungsreihenfolge
1. Manifest-Format, Fingerabdruck, Architekturtest mit Ausnahmeliste für Altbestand.
2. Adapter nacheinander umbauen, bis die Ausnahmeliste leer ist; `DiviCompatibility` auf Manifeste umstellen; Docblock-Zeilennummern entfernen.
3. Zustand, Freigabe (Code-Liste, Datenbank), zentrale und Adapter-Sperre, `lit-site-status`.
4. Admin-Reiter „Divi“, Prüfschritte, Wegwerfseite, Bericht, Freigabe.
5. WP-CLI-Befehl mit `--diff` und `--update-reference`.
6. Security-Audit; danach Etappe 4.