Canvas-Config-Drift-Heilung via solcom_editorial post_update Hooks
prop_field_definitions mit Empty-Array-default_value für Non-Entity-Ref-Fields) werden via hook_post_update_N in solcom_editorial generisch und idempotent geheilt. Jeder Deploy via drush updb repariert; kein händlicher Eingriff pro Inzidenz.
Kontext
Drupal-Canvas hält für jedes SDC-Placement zwei Versions-bezogene Storage-Layer: (1) die Component-Config-Entity canvas.component.sdc.<theme>.<name> mit active_version + versioned_properties-Hash-Map; (2) die Page-Trees in canvas_page__components / node__field_component_tree mit components_component_version-Pin pro Placement. Beide Layer werden ergänzt durch:
- einen Autosave-Tree in
key_value.canvas.auto_savepro Page bzw. Page-Region, der unpublishte Edits hält; - die Field-Inferenz, die Canvas beim Discover einer SDC vornimmt: SDC-Schema-Prop → Drupal-Field-Type-Mapping +
default_value-Initialisierung.
Beide Schichten driften gegen die SDC-Schema-Wahrheit, sobald die SDC sich entwickelt:
- Orphan-Prop-Keys im Autosave. Beispiel:
byte_theme/footerhatte mal einenalign-Prop, der wurde später aus der SDC entfernt. Ein älterer Autosave-Draft trägtinputs: {"align": true}weiterhin. Beim Editor-Open für die betroffene Page wirft Canvas'align' is not a prop on this version of the Component …und crasht den Editor mit 500. - Falsche
default_value-Inferenz. Beispiel:byte_theme/skills-pillsdeklariertskillsalstype: array | null. Die Canvas-Inferenz mapped das auffield_type: stringmitdefault_value: [](Empty-Array). Eine spätere Canvas-Update-Version führt die AssertionexampleValueRequiresEntity($shape) === ($default_value === [])inGeneratedFieldExplicitInputUxComponentSourceBase::getDefaultStaticPropSource()ein — bei Non-Entity-Ref-Field mit Empty-Array-Default ist die AssertionFALSE === TRUE→ 500 beim Component-Picker-Fetch.
Beide Klassen sind vorhersagbare Folge-Drift, keine Bugs in der Schema-Schicht. Sie können auf jedem Environment (Dev, Staging, Prod) auftreten, sobald eine SDC sich entwickelt. ADR-0002 hat den ersten Drift-Fall (Page-Region-Versions-Pin gegen entfernten Slot) per einmaligem Repair-Script geheilt — aber pro-Inzidenz, nicht generisch.
Entscheidung
Zwei Drift-Klassen werden per hook_post_update_N im solcom_editorial-Modul generisch und idempotent geheilt. Datei: web/modules/custom/solcom_editorial/solcom_editorial.post_update.php. Lauf-Zeitpunkt: drush updb bei jedem Deploy. Schema-Version-Bump per Hook stellt sicher, dass jeder Hook genau einmal pro Environment ausgeführt wird; ein zweiter Lauf nach Re-Set der Schema-Version wäre no-op (Idempotenz im Hook-Body).
Hook 1 — prune_orphan_autosave_props
Durchläuft alle canvas.auto_save-Einträge im Key-Value-Store. Für jede SDC-Component im component_tree (Block-Components werden übersprungen, weil deren Inputs nicht schema-versioniert sind):
- Schlägt die
prop_field_definitionsder gepinnten Component-Version auscanvas.component.<id>.versioned_properties.<ver>.settings.prop_field_definitionsnach (Fallback:active). - Bildet die Differenz zwischen den
inputs-Keys des Drafts und den definierten Prop-Namen. - Entfernt jeden Orphan-Key aus der JSON-encoded
inputs-String und schreibt den Tree zurück in den Autosave-Eintrag.
Hook 2 — repair_orphan_default_values
Durchläuft alle canvas.component.<id>-Configs. Pro Version, pro Prop-Field-Definition:
- Falls
field_typenichtentity_referenceODERimageist UNDdefault_value === [], wirddefault_valueauf[['value' => NULL]]umgeschrieben. Twig-is iterable-Checks im SDC handeln den Null-Case sauber ab; die Canvas-Assertion ist erfüllt ([[null]] !== []). - Idempotent: ein zweiter Lauf findet keine Empty-Array-Defaults mehr und ändert nichts.
Operativer Workflow
# Dev-Environment (oder beliebige andere) drush updb -y # Output: # > [notice] Update started: solcom_editorial_post_update_prune_orphan_autosave_props # > [notice] Canvas-Autosave-Cleanup: 1 Draft-Eintrag/e beruehrt, 1 Orphan-Input-Key(s) entfernt. # > [notice] Update started: solcom_editorial_post_update_repair_orphan_default_values # > [notice] Canvas-Default-Value-Repair: 1 Component-Config(s) repariert.
Bei zukünftigen Drift-Klassen (z. B. versioned_properties-Eintrag fehlt, aber Page-Tree pinnt darauf) wird eine dritte Hook-Funktion mit eigenem Namen angehängt. Schema-Version wächst monoton, jeder Hook läuft genau einmal pro Environment.
Rejected alternatives
- Direkter SQL-Patch / Drush-Php-Script pro Inzidenz. Schnell zu schreiben, aber nicht in Git versioniert auf andere Environments übertragbar. Manuelle Anwendung pro Environment ist fehleranfällig und intransparent für Deploy-Log-Audit. Verworfen.
-
EventSubscriber auf
kernel.request, der bei jedem Page-Load Autosave-Drafts scannt. Performance-Risiko (Key-Value-Walk pro Request), unklarer Run-Zeitpunkt, schwer zu debuggen.hook_post_update_Nhat klare Lifecycle-Semantik (drush updb) und ist genau dafür gedacht. Verworfen. - Patch im Canvas-Module (Vendor) für die Assertion. Verworfen: Patching Vendor-Code blockiert Composer-Updates und maskiert die symptom-Ursache statt sie zu fixen. Die Assertion ist defensiv gut — das Drift-Symptom gehört im SOLCOM-Custom-Code geheilt.
-
SDC-Schema von
skills-pillsauftype: stringsimplifizieren, damit die Inferenz korrekt mapped. Sinnvoll als Folge-Aufgabe, aber adressiert nur EINEN konkreten Fall, nicht die generelle Drift-Klasse. Andere SDCs mitarray | null-Props würden weiterhin den gleichen Default-Value-Crash produzieren. Der Post-Update-Hook fängt alle Fälle ab.
Consequences
- Deploy-Workflow: Auf jedem Environment läuft
drush updbals Teil des Standard-Deploy-Tails (Mittwald-Deploy-Hook bzw. Azure-DevOps-Pipeline). Die Hooks laufen genau einmal pro Environment ohne weitere Operator-Aktion. - Beobachtbarkeit: Hook-Output gibt Touch-Counts zurück (
n Draft-Eintrag/e beruehrt,m Component-Config(s) repariert) — direkt im Deploy-Log nachvollziehbar. - Sub-Skript-Trennung: Block-Components (
block.*) und JS-Components (js_component.*) werden in Hook 1 bewusst übersprungen, weil deren Inputs nicht schema-versioniert sind. Wenn ein Block-Plugin sein Settings-Schema ändert, gilt das Standard-Canvas-Repair-Pattern aus ADR-0002 — nicht dieser Hook. - Idempotenz garantiert: ein erneuter Hook-Run (z. B. nach manuellem
drupal_set_installed_schema_version()-Reset zu Debug-Zwecken) findet die bereits-bereinigten Trees + Configs unverändert vor und macht nichts. - Modul-Wahl
solcom_editorial: dort liegt schon der Editor-bezogene Custom-Code (Scheduler-Hide-Logic für Users ohne Transitions). Card-Frame, Footer, Skills-Pills sind alle Editor-Surface-relevant. Alternative wäre ein neues Modulsolcom_canvas_repair— vermeidet, weil das Modul-Ekosystem ohnehin überschaubar ist und ein Single-Concern-Repair-Modul keine eigene.info.ymlrechtfertigt. - Reversibilität: Die Hooks ändern nur Drift-Daten; sie sind weder Schema-Migrationen noch destructive Edits. Rollback wäre Restore aus Snapshot. Praktisch unnötig, weil die Hook-Logik konservativ ist (nur Keys löschen, die laut Schema nicht existieren; nur Empty-Array-Defaults patchen).