Files
WP-LIT-MCP/docs/superpowers/notes/2026-09-26-etappe-2-entscheidungen.md
T

7.0 KiB
Raw Blame History

Etappe 2 – Entscheidungen der Planung

Getroffen bei der Planung von Etappe 2a/2b, 2026-09-26. Kosten, falls falsch, jeweils am Ende.

  • D1 (Ruling P16): rev = Rev::ofPost (ganzer Eintrag); Divi-Sperre = tree_rev = Rev::of(post_content). Divi-Lese-Tools liefern beide, Divi-Schreib-Tools verlangen tree_rev und liefern beide neu. – Kosten: keine.
  • D2: Direkte Tools nach Etappe 2 = 21 (Spec §4.3 ohne lit/library-insert, plus lit/site-status, lit/media-download). lit/site-status bleibt direkt (leeres Schema, Anker von mcp-smoke.sh), lit/media-download muss direkt bleiben. Indirekt: lit/divi-duplicate-node, lit/divi-replace-text. – Kosten: etwas mehr Kontext je Unterhaltung; in Etappe 3 neu bewerten.
  • D3: BlockTree mit eigenem Tokenizer (Muster von WP_Block_Parser), opake Knoten für globale Module/Layouts und undekodierbare Attribute, unbalanciertes Markup → Ablehnung. – Kosten: globale Module erst ab Etappe 3 bearbeitbar.
  • D4: ExposurePolicy::DIRECT_ONLY bleibt eine Liste; Etappe 2 bringt keine Bild-/Ressourcen-Tools, ein Ability::requiresDirect() wäre YAGNI. – Kosten: bei weiteren Bild-Tools die Liste ergänzen.
  • D5: lit/page-render-summary gehört zur WordPress-Integration (funktioniert für jeden Eintrag) und rendert den Inhaltsbereich im Prozess (the_content), ohne HTTP-Loopback. – Kosten: Header/Footer/Theme Builder fehlen im Auszug.
  • D6: lit/divi-dynamic-sources und die Warnung „ähnliche Abschnitte“ kommen mit Etappe 3 (Theme Builder, lit/library-find-similar). – Kosten: bis dahin keine Quellenliste; {{field:…}} nimmt jeden Meta-Schlüssel.
  • D7 (Ruling R1): Divi-Speichern ohne kses für Benutzer ohne unfiltered_html (sonst re-serialisiert WordPress jeden Block) ist nur vertretbar, weil keine client-gelieferte Zeichenkette ungeprüft in post_content gelangt. Klartext-Werte werden je Feldtyp bereinigt (Text: sanitize_text_field; Rich-Text: wp_kses_post, abhängig von unfiltered_html; URLs: esc_url_raw, geroutet über die Attribut-Metadaten des Katalogs, ersatzweise über den Schlüsselnamen). Werte mit $variable(...)$ – ob aus Platzhaltern erzeugt oder unverändert aus lit/divi-node-get zurückgegeben – werden geparst, streng geprüft (zulässiger Typ, existierende gcid-/gvid-/Dynamic-Content-Quellennamen, whitelisted Settings-Schlüssel, String-Settings bereinigt) und vom Plugin neu serialisiert; alles andere wird mit validation_failed abgelehnt. Code-Modul ungefiltert nur mit unfiltered_html. – Kosten: jeder neue Schreibpfad muss bereinigen, streng validieren und neu serialisieren (Review-Pflicht).
  • R2 (Ruling): Ob Divi das Dynamic-Content-before/after bei der Ausgabe selbst escaped, wird in Etappe 2b im Divi-Quellcode verifiziert; die Bereinigung aus D7 gilt unabhängig vom Ergebnis (Verteidigung in der Tiefe). – Kosten: keine (Verifikation, keine Abhängigkeit der Sicherheit vom Ergebnis).
  • R2 – Befund (Etappe 2b, Divi 5.2.1, Ruling S5): (1) Dynamic-Content-before/after wird bei der Ausgabe nur dann mit wp_kses gefiltert, wenn der Autor des Beitrags kein unfiltered_html hat (DynamicContentElements::get_wrapper_element); auf Seiten eines Administrators gelangt es ungefiltert ins HTML – ein Redakteur könnte dort Skript platzieren. (2) enable_html: on gibt Meta-Werte mit wp_kses_post statt esc_html aus; Divi entfernt enable_html beim Speichern nur für Benutzer ohne unfiltered_html (Security::sanitize_dynamic_content_fields, wp_insert_post_data Priorität 10). (3) custom_meta_<key> wird ohne is_protected_meta-Prüfung ausgegeben (DynamicContentOptionPostMetaKey). (4) Farbverweis-Namen gehen unescaped in var(--<name>) (StyleLibrary\Utils::resolve_dynamic_variable). (5) Divis eigene wp_insert_post_data-Filter sind kein kses und laufen auch bei unserem Speichern ohne kses: AttributeSecurity (Priorität 5, immer, sobald "decoration":{ und "attributes":{ vorkommen) und HtmlSecurity (Priorität 6, bei htmlBefore/htmlAfter/elementType) parsen und re-serialisieren die ganze Seite; DynamicContentFixes (Priorität 10). Folgen: die Plugin-Prüfung aus D7 ist die eigentliche Garantie (VariableRef: nur ganze, geprüfte, neu serialisierte Verweise; before/after ohne unzulässiges HTML, kein enable_html, keine Meta-Schlüssel mit _); Divis Filter bleiben aktiv (Verteidigung in der Tiefe) und werden nie entfernt; Schreiben auf *.decoration.attributes und *.advanced.html nur mit unfiltered_html („nur im Divi-Builder“, Ruling S7), und auf Seiten mit diesen Feldern gilt die Byte-Invarianz nicht (Divi normalisiert beim Speichern). – Kosten: keine (Dokumentation; die Sicherheit hängt nicht von Divis Ausgabe ab).
    • Nachtrag (Etappe 2b, Task 10, live): Divis AttributeSecurity/HtmlSecurity serialisieren mit serialize_blocks(); Bytes ändern sich nur, wenn die Seite noch nicht in dieser Form gespeichert war (z. B. Import, direkter DB-Schreibzugriff). Über Builder oder LIT MCP gespeicherte Seiten bleiben außerhalb der Änderung byte-identisch (E2E divi-write-smoke.sh 8a/8b).
  • D8: Spalten neuer Zeilen über module.decoration.sizing.desktop.value.flexType ("1_3,2_3" → 8_24, 16_24; Fünftel und 1/7, 1/9, 1/10, 1/11 nativ), wie Divi 5.2 (Flex-Layout ist Standard). – Kosten: falls der Builder anders speichert, nur Composer anpassen.
  • D9: Platzhalter stehen allein im Wert; Überschriften der Kurzform ohne level werden h2. – Kosten: eingebettete dynamische Texte nur über die Vollform.
  • D10: lit/divi-replace-text je Inhaltstyp; Probelauf liefert tree_rev je Seite, Ausführung nur auf diesen targets mit confirm: true; ersetzt nur sichtbaren Text (…innerContent, Schlüssel text/title/label/alt/titleText), nie URLs oder Stile. – Kosten: Text mit HTML-Entitäten muss so gesucht werden, wie er gespeichert ist.
  • D11: lit/content-create mit divi speichert über wp_insert_post mit WordPress' üblicher Filterung (nur neuer, selbst erzeugter Inhalt – nichts Unberührtes, das byte-identisch bleiben müsste). – Kosten: für Benutzer ohne unfiltered_html kann WordPress Entitäten normalisieren; tree_rev in der Antwort beschreibt den gespeicherten Stand.
    • Überholt (Etappe 2b, Task 9): Titel, Auszug und übrige Felder laufen weiter durch wp_insert_post mit üblicher Filterung; das Divi-Layout wird danach über DiviSaver::saveNew im selben kses-Fenster wie alle Divi-Schreibvorgänge gespeichert (D7, ein einziger Speicherweg). Grund: WordPress' kses würde die $variable-Verweise und Blockattribute sonst für Benutzer ohne unfiltered_html verändern; bereinigt wird vorher je Feldart (AttrWriter/VariableRef).
  • D12: Fixtures: handgeschriebene Grenzfälle (synthetic-*.html), mit Divis eigener Konvertierung erzeugte Seiten (converted-*.html), echte Modul-Metadaten (metadata-sample.json); im Visual Builder gebaute Seiten exportiert Jan-Niclas später mit tests/Integration/divi-export-page.php als builder-*.html – sie laufen automatisch im Roundtrip-Test mit.