# Bedienungsanleitung Drive DataEngine
## 1. Zweck
Drive DataEngine ist eine Symfony-basierte Kontrollschicht fuer den bestehenden Legacy-ETL-Bestand.
Die Plattform stellt aktuell bereit:
- Dashboard unter `https://dataengine.domgan.de`
- modernisierte server-renderte Operator-Oberfläche mit dunklem Control-Plane-Shell-Layout, linker Rail-Navigation, kontextueller Top-Bar und ausgerolltem Redesign über Übersicht, Workbench, Infrastruktur, Editor, Kompatibilität, Legacy-YAML-Sicht, Run-Detail und Handbuch
- responsive gehärtete Operator-Oberfläche ohne Body-Level-Horizontalscroll auf den geprüften Kernrouten; große Tabellen bleiben auf Smartphones lesbar und lange technische Werte umbrechen kontrolliert
- oeffentlich lesbare Bedienungsanleitung unter `https://dataengine.domgan.de/manual`
- tenant-isolierte Run-Historie in PostgreSQL mit RLS
- CLI-Commands fuer Katalog, Verbindungsprofile, Run-Planung und Ausfuehrung
- direkte Run-Buttons für aktive Managed Pipelines, aktive Managed Engines und gültige Legacy-YAML-Definitionen
- kontrollierte Legacy-Ausfuehrung ueber die interne Bridge
- erste native Symfony-Runtime fuer Legacy-Datenbankprofile auf MySQL- und PostgreSQL-Basis
- erste native MySQL-Datapool-Struktur-, CSV- und JSON/YAML-Importpfade fuer Betrieb und Migration
- tenant-faehige Managed Definitions Workbench fuer Engines und Pipelines in PostgreSQL
- Commerce-Template-Katalog mit DataEngine Commerce Product v1 Standard, Importstartern, Channel-Export-Templates und installierbaren Pipeline-Recipes
- erste native Symfony-Ausfuehrung fuer Managed Import-Engines sowie fuer eine wachsende native Transform-Runtime ueber `unitize`, `enhance` und `export`, inklusive Mapping-, SQL-, Join-, Filter- sowie erster `Field_Adapter`-, `Cross_Field_Adapter`-, `Cross_Dataset_Filter_Adapter`- und `File_Field_Adapter`-Teilsemantik mitsamt kontrolliertem Additional-Datapool-Loading in tenant-isolierte PostgreSQL-Staging-Datapools
- tenant-aware MCP-Endpunkt fuer den Remote-Zugriff auf den Control Plane
- repo-lokales Codex-Plugin zum Einbinden dieses MCP-Endpunkts aus anderen Codex-Projekten
## 1.1 Grundsatz: Tool vs. Anwendung
Es wird strikt unterschieden zwischen:
- `Tool-Implementierung`
Symfony-Code, Docker-Setup, Datenbankschema, Runtime-Services, Bridge, Commands und GUI-Logik
- `Anwendung`
konkrete Engines, Pipelines und deren YAML-Inhalte
Die Anwendungsdefinitionen gehoeren nicht in den Tool-Code. Neue Engines und Pipelines werden deshalb schrittweise als tenant-isolierte Daten in PostgreSQL verwaltet.
## 2. Aktueller Architekturstand
### 2.1 Komponenten
- `dashboard`
Symfony-Weboberflaeche und Symfony-CLI
- `legacy-bridge`
startet bestehende Legacy-Runner kontrolliert per HTTP
- `postgres`
speichert Tenants, Runs und synchronisierte Connection-Metadaten
- `traefik`
bindet `dataengine.domgan.de` von aussen an
### 2.2 Legacy vs. native Runtime
Aktueller Stand:
- Pipelines und Engines werden tenant-spezifisch aus Managed Definitions und Legacy-YAML kombiniert, nativ in Symfony erkannt und vorvalidiert.
- Managed Import-Engines mit `Source_Connection_Type: file` und Ziel `native_runtime`, `native_staging` oder dem Legacy-Alias `importEngine` laufen bereits nativ in Symfony.
- Managed Transform-Engines der Typen `unitize`, `enhance` und `export` koennen in einer wachsenden nativen Teilmenge ebenfalls in Symfony laufen, wenn `Source_Config` und `Target_Config` auf die nativen Staging-Aliasse oder die vertrauten Legacy-Aliasse wie `importEngine`, `unitizeEngine`, `enhanceEngine` und `exportEngine` zeigen.
- Managed Pipelines koennen nativ laufen, wenn alle Schritte ueber die native Staging-Runtime unterstuetzt werden, derzeit insbesondere File-Import plus native Transform-Teilpfade fuer `unitize`, `enhance` und `export`.
- Alle uebrigen fachlichen Pfade laufen weiterhin ueber die Legacy-Bridge.
- Managed Definitions werden fuer Legacy-Runs als isolierter Overlay-Snapshot materialisiert und nicht in den Legacy-Bestand geschrieben.
- Legacy-Datenbankprofile koennen in Symfony bereits nativ bewertet werden.
- Fuer MySQL- und PostgreSQL-Profile existieren read-only Runtime-Probes; MySQL-Profile haben zusaetzlich Datapool-Inspektion sowie erste Write-, Import- und Strukturkopier-Operationen.
- ODBC-Profile bleiben vorerst im Legacy-Pfad.
## 3. Zugriff
### 3.1 Web
- URL: `https://dataengine.domgan.de`
- aktueller Status: oeffentlich erreichbar ueber Traefik
- Handbuch: `https://dataengine.domgan.de/manual`
- öffentliche AI-Discovery-Dateien: `https://dataengine.domgan.de/llms.txt`, `https://dataengine.domgan.de/site-context.md`, `https://dataengine.domgan.de/ai-discovery.md`, `https://dataengine.domgan.de/manual.md`
- Infrastruktur: `https://dataengine.domgan.de/infrastructure`
- Scheduler: `https://dataengine.domgan.de/scheduler`
- Product Master: `https://dataengine.domgan.de/commerce-products`
- Product Master Live-Diff: `https://dataengine.domgan.de/commerce-products/diff`
- Ortho-Vertrieb Google-Merchant-Feed: `https://dataengine.domgan.de/feeds/google/ortho-vertrieb-products.tsv`
- Commerce-Templates: `https://dataengine.domgan.de/commerce-templates`
- Legacy-YAML-Browser: `https://dataengine.domgan.de/legacy-definitions`
- YAML-Kompatibilitaet: `https://dataengine.domgan.de/compatibility`
- MCP: `POST https://dataengine.domgan.de/mcp`
### 3.2 Server
- Deployment-Ziel: `ortho-live`
- Projektpfad auf dem Server: `/opt/containers/drive-dataengine`
- Compose-Datei: `/opt/containers/drive-dataengine/compose.yaml`
- Dashboard-Healthcheck: `GET /healthz`; dieser leichte Endpunkt wird von Docker genutzt, damit Traefik den Webcontainer auch bei sehr großen Dashboard-Seiten stabil routen kann.
### 3.3 Codex Plugin
Im Repository liegt zusaetzlich ein repo-lokales Codex-Plugin:
- Plugin-Pfad: `plugins/dataengine-mcp`
- MCP-Config: `plugins/dataengine-mcp/.mcp.json`
- Marketplace-Eintrag: `.agents/plugins/marketplace.json`
Das Plugin ist bewusst repo-lokal versioniert. Dadurch kann es aus anderen Codex-Projekten referenziert oder installiert werden, ohne dass in diesem Umsetzungsschritt globale Codex-Dateien ausserhalb des Repositories veraendert werden.
## 4. Mandantenmodell
Die Multi-Tenant-Isolation gilt derzeit fuer die Kontrollschicht:
- `tenants`
- `execution_runs`
- `connection_profiles`
- `managed_definitions`
- `managed_definition_revisions`
- `native_staging_datapools`
- `native_staging_datapool_rows`
Die Isolation erfolgt ueber PostgreSQL Row Level Security und den gesetzten Tenant-Kontext in Symfony.
Dieselbe Tenant-Isolation gilt auch fuer den MCP-Layer. MCP-Tools loesen den Tenant ueber ihr Argument `tenant` auf und arbeiten danach ueber dieselben Symfony-Repositories und Services wie GUI und CLI.
## 5. Dashboard bedienen
### 5.1 Startseite
Die Startseite zeigt:
- die neue linke Rail-Navigation fuer `Übersicht`, `Workbench`, `Commerce`, `Infrastruktur`, `Legacy YAML`, `Kompatibilität` und `Handbuch`
- eine kontextuelle Top-Bar mit Seitenstatus, Tenant-Kontext und Primäraktionen
- einen Control-Plane-Einstieg mit Schnellaktionen für Workbench, Infrastruktur, neue Pipelines, Legacy-YAML-Browser, Kompatibilitätsansicht und Live-Handbuch
- Anzahl erkannter Pipelines und Engines
- Tenant-Kontext
- Runtime-Katalog aus Managed Definitions und Legacy-Bestand
- erkannte Legacy-Verbindungsprofile
- native Datenbank-Runtime-Abdeckung
- native Datapool-Write-Abdeckung
- native Staging-Runtime fuer Managed File-Importe und native Transform-Teilpfade fuer `unitize`, `enhance` und `export`
- Managed-Definitions-Status aus PostgreSQL
- letzte tenant-isolierte Ausfuehrungen
- alle gefundenen Pipelines
- alle gefundenen Engines pro Runner-Typ, nun als einklappbare Abschnitte statt als unstrukturierte Vollansicht
- direkte `Pipeline ausführen`- und `Engine ausführen`-Buttons im Runtime-Katalog
- Verweis auf die YAML-Kompatibilitaetsansicht
- native Datapool-Formulare fuer Drop, Truncate, Blueprint, CSV, Datenableitung und LOAD DATA
### 5.1.1 Responsive Bedienung
Die Operator-Oberfläche ist für Desktop, Laptop, Tablet und schmale Smartphone-Breiten ausgelegt.
Wichtig für die Bedienung:
- Auf den geprüften Kernrouten entsteht kein horizontaler Seiten-Scroll auf Body-Ebene.
- Lange technische Werte wie UUIDs, Dateipfade, Runner-Namen, Datapool-Namen und Feed-URLs umbrechen innerhalb ihrer Karten, Tabellenzellen und Badges.
- Dichte Tabellen bleiben auf breiten Viewports als Tabellen sichtbar.
- Auf schmalen Viewports werden Tabellenzeilen automatisch als gelabelte Kartenzeilen dargestellt, damit Spaltenüberschriften nicht verloren gehen.
- Große Inventarbereiche wie Runtime-Kataloge und Legacy-Engine-Gruppen sind standardmäßig eingeklappt oder in ihrer Höhe begrenzt, damit die Startseite nicht unnötig lang wird.
- Alle Inhalte bleiben erreichbar; bei sehr großen Datenbereichen wird innerhalb des jeweiligen Bereichs gescrollt statt die gesamte Seite horizontal zu verbreitern.
### 5.5 Bedienungsanleitung im Dashboard
Unter `https://dataengine.domgan.de/manual` wird immer die aktuelle Datei `docs/bedienungsanleitung.md` aus dem ausgelieferten Stand angezeigt.
Damit gilt:
- Jede Aenderung an Bedienung, Commands oder Betrieb muss die Markdown-Datei aktualisieren.
- Nach jedem Deployment ist die Live-Anleitung automatisch synchron mit dem Container-Stand.
- Die Seite läuft inzwischen im selben Control-Plane-Shell-System wie die übrigen Operator-Screens.
### 5.6 Native Datapool-Operationen im Dashboard
Im Bereich `Native Datapool Operations` stehen direkte Formulare fuer die nativen MySQL-Pfade bereit.
Unterstuetzt:
- Datapool loeschen
- Datapool leeren
- Datapool aus Blueprint-Datei erzeugen
- Datapool aus CSV-Kopfzeile erzeugen
- Datapool aus JSON- oder YAML-Daten ableiten
- JSON- oder YAML-Daten per Multi-Insert in bestehende Datapools laden
- Datapool-Strukturen von bestehenden Tabellen kopieren
- CSV per `LOAD DATA LOCAL INFILE` in bestehenden Datapool laden
Wichtig:
- Die Dateipfade muessen aus Sicht des Dashboard-Containers existieren.
- Mitgelieferte Beispielpfade liegen unter `/srv/app/docs/examples/`.
- Dashboard und CLI nutzen dieselben Symfony-Services, damit Bedienung und Ausfuehrungslogik synchron bleiben.
### 5.7 Managed Definitions Workbench
Unter `Definitionen` im Kopfbereich liegt die neue Anwendungsebene.
Der Einstieg erfolgt jetzt über die linke Rail-Navigation unter `Workbench`. Innerhalb der Seite führt die Top-Bar zusätzlich über die Bereiche `Überblick`, `Authoring` und `Bestand`.
Dort werden Engines und Pipelines als tenant-faehige Daten gepflegt:
- neue Pipelines und Engines aus Starter-Templates anlegen
- beliebig viele Engines je Runner-Typ und beliebig viele Pipelines tenant-spezifisch anlegen
- YAML im Browser bearbeiten
- bei Pipelines vorhandene Managed Engines über eine globale Suche oder typbezogene Picker direkt in den `Engines`-Block einfügen
- Validierungshinweise direkt beim Speichern sehen
- Revisionen pro Definition nachhalten
- Draft-, Active- und Archived-Status setzen
- die mitgelieferten Templates fuer Import und Unitize bleiben bewusst nah am Legacy-YAML-Stil und verwenden standardmaessig `importEngine` beziehungsweise `unitizeEngine`
- Editor, Metadaten, Revisionen und Validierung sind in einer gemeinsamen Operator-Ansicht zusammengefuehrt
- Die Detailbearbeitung einzelner Definitionsdatensätze nutzt denselben Shell-Stil und führt zusätzlich über die Bereiche `Überblick`, `Editor`, `Validierung`, `Metadaten` und `Revisionen`.
Wichtig:
- Diese Definitionen sind bewusst getrennt vom Tool-Code.
- Sie werden derzeit in PostgreSQL gespeichert und versioniert.
- `Active`-Definitionen fliessen tenant-spezifisch direkt in Runtime-Katalog, Planung und Ausfuehrung ein.
- `Active`-Definitionen können direkt aus der Workbench-Liste und aus der Detailbearbeitung über `Run` beziehungsweise `Run starten` ausgeführt werden.
- `Draft`- und `Archived`-Definitionen bleiben in der Workbench sichtbar, werden aber nicht ausgefuehrt und zeigen stattdessen einen nicht-aktiven Hinweis.
- Die Workbench ist die kuenftige Quelle fuer neue Anwendungen; die Legacy-YAML-Dateien bleiben vorerst lesbar als Referenzbestand.
### 5.8 Commerce Templates bedienen
Unter `https://dataengine.domgan.de/commerce-templates` liegt der neue Commerce-Template-Katalog.
Der Katalog enthält:
- den kanonischen Enhance-Standard `DataEngine Commerce Product v2`
- CSV-Importstarter für generische Artikeldaten und Google-Merchant-nahe CSVs
- API-Pull-Starter für CommerceOps ERP, Shopify, Shopware 6, Adobe Commerce / Magento, WooCommerce und Akeneo
- Export-Templates für Google Merchant, idealo, günstiger.de, billiger.de, Kelkoo, Amazon, OTTO, Kaufland, CHECK24, eBay, Rakuten, Meta, Pinterest, TikTok, Criteo, Awin, Shopware 6 und Shopify
- installierbare Pipeline-Recipes, die Import, Standardisierung und Channel-Export als Managed Drafts anlegen
Wichtig:
- Die Template-Dateien sind Tool-Implementierung und werden nicht direkt in der GUI editiert.
- Ein Klick auf eine Recipe legt tenant-isolierte Managed Drafts in PostgreSQL an.
- Bestehende Definitionen werden standardmäßig übersprungen; nur mit aktivierter Aktualisierung werden Template-Drafts überschrieben.
- Die Export-Engines erzeugen channel-ready native Staging-Datapools. Datei-, SFTP- oder API-Auslieferung bleibt ein separater Push-/Connection-Profile-Schritt.
- API-Importstarter benötigen vor Ausführung ein tenant-isoliertes `ready` Connection-Profil.
Der Standard ist dokumentiert unter `docs/commerce-template-plan.md`. Die YAML-Standarddefinition liegt unter `symfony/config/commerce_templates/definitions/enhance/commerce_enhance_product_standard_v1.yaml`.
### 5.8.1 Ortho-Vertrieb Product Master bedienen
Unter `https://dataengine.domgan.de/commerce-products` liegt die zentrale Pflegefläche für den Commerce Product Master. Sobald der ERP-Import vollständig ist, liest die Ansicht `commerce_product_master_v2`; der alte Shop-Snapshot bleibt nur als Fallback und Vergleichspfad erhalten. Auf `ortho-live` ist zusätzlich `DATAENGINE_ERP_MIN_PRODUCTS=30` gesetzt, damit ein Teilimport nicht versehentlich als produktiver ERP-Master angezeigt oder exportiert wird.
Der Datapool heißt:
- `commerce_product_master_v2` für den ERP-geführten Zielzustand
- `ortho_vertrieb_product_master_v1`
Wichtig:
- Das ERP ist führend für Produkttexte, Produktinformationen, Zutaten, Bilder, Mehrwertsteuersätze, Kostenbasis, Lager und Status.
- Die DataEngine erzeugt daraus channel-spezifische Projektionen. Google-Titel, Marktplatz-Kategorien, MwSt.-Zielformate oder Channel-Ausschlüsse sind Channel Overrides und keine zweite Wahrheit.
- Der Import schreibt ausschließlich in die DataEngine-PostgreSQL-Datenbank des aktuellen Tenants.
- Der Rückweg in den Shop läuft über einen geprüften Sync-Plan und ein Importartefakt, nicht über direkte ungeprüfte Datenbankänderungen.
Die Oberfläche bietet:
- Suchfeld über SKU, Titel, Marke, MPN, Produkttyp und Labels
- Produktliste mit Preis, Verfügbarkeit, Bestand und Qualitätsstatus
- Detailmaske nach Commerce Product Feldgruppen
- Read-only Ansicht für `commerce_product_master_v2`, weil ERP-Stammdaten nicht lokal überschrieben werden dürfen
- Anlage neuer Produktzeilen nur im alten DataEngine-Arbeitskopie-/Snapshot-Pfad, nicht im ERP-geführten v2-Master
- Pflege von Channel Overrides unter `https://dataengine.domgan.de/commerce-products/overrides`
- read-only Live-Shop-Diff gegen einen eingefügten JSON-Snapshot
- CSV-Export des Product-Master-Datapools über die Infrastrukturansicht
Channel Overrides in der Oberfläche:
- Ein Override gilt für eine SKU, Produkt-ID, Item-Group-ID oder ERP-SKU und einen Channel.
- Einfache Fälle werden als `field` und `value` gepflegt, mehrere Felder als JSON-Objekt im Feld `overrides`.
- Aktive Regeln haben Status `active`, `approved`, `published` oder leer. `draft`, `disabled` und `archived` bleiben sichtbar, werden aber nicht angewendet.
- Nach jeder Änderung muss die betroffene Projektion neu laufen, zum Beispiel `app:commerce:project-channel google_merchant --commit`.
Der sichere ERP-Importablauf ist:
1. Im ERP Produkte und Bestände pflegen.
2. DataEngine mit einer API-Quelle oder JSON-Datei versorgen.
3. Den ERP-Import per Dry-Run und Mindestproduktzahl prüfen.
4. Erst danach mit `--commit` in `commerce_product_master_v2` importieren.
5. Channel Overrides nur für kanalbezogene Abweichungen nutzen.
Für den initialen Ortho-Vertrieb-Bootstrap existiert ein eigener Command, der den geprüften DataEngine Product Master einmalig in den ERP-Tenant schreibt. Er darf nur bewusst genutzt werden, um den ERP-Startbestand herzustellen:
```bash
php /srv/app/symfony/bin/console app:commerce:bootstrap-erp-from-product-master --tenant=default --source-datapool=ortho_vertrieb_product_master_v1 --erp-tenant=ortho-vertrieb --sync-stock
php /srv/app/symfony/bin/console app:commerce:bootstrap-erp-from-product-master --tenant=default --source-datapool=ortho_vertrieb_product_master_v1 --erp-tenant=ortho-vertrieb --sync-stock --commit
```
Der produktive ERP-Import nutzt danach die API-Quellen aus `.env`:
```bash
php /srv/app/symfony/bin/console app:commerce:import-erp-products --tenant=default --erp-tenant=ortho-vertrieb --min-products=30 --json
php /srv/app/symfony/bin/console app:commerce:import-erp-products --tenant=default --erp-tenant=ortho-vertrieb --min-products=30 --commit --json
```
Aktueller Stand: Der ERP-Tenant `ortho-vertrieb` ist gebootstrappt und der DataEngine-Token ist serverlokal konfiguriert. Der produktive ERP-Cutover bleibt blockiert, bis die ERP-API `/api/v1/products` einen vollständigen oder paginierten Export liefert; aktuell kommen nur 20 Produkte zurück.
Beispiel mit Dummy-Daten:
```bash
php /srv/app/symfony/bin/console app:commerce:import-erp-products docs/examples/erp-products.example.json --stock-source=docs/examples/erp-stock.example.json --tenant=default --min-products=1 --commit
php /srv/app/symfony/bin/console app:commerce:load-channel-overrides docs/examples/channel-overrides.example.json --tenant=default --commit
php /srv/app/symfony/bin/console app:commerce:project-channel ortho_shop --tenant=default --commit
php /srv/app/symfony/bin/console app:commerce:build-repricing-plan docs/examples/repricing-snapshot.example.json --channel=ortho_shop --tenant=default --commit
php /srv/app/symfony/bin/console app:commerce:export-ortho-shop-sync-plan --tenant=default --commit --target-file=/srv/app/symfony/public/feeds/ortho-shop/ortho-vertrieb-shop-sync.csv
php /srv/app/symfony/bin/console app:commerce:project-channel google_merchant --tenant=default --target-datapool=commerce_channel_google_merchant_products_v1 --commit
php /srv/app/symfony/bin/console app:commerce:export-ortho-google-merchant-feed --tenant=default --skip-run --datapool=commerce_channel_google_merchant_products_v1
```
Der alte Shop-Snapshot-Import bleibt möglich, wenn der Live-Shop mit dem DataEngine-Master verglichen werden soll:
Der sichere Vergleichsablauf vor jedem späteren Shop-Rückweg ist:
1. Auf `ortho-live` einen frischen read-only JSON-Snapshot aus der Shop-Datenbank erzeugen.
2. Unter `https://dataengine.domgan.de/commerce-products/diff` den Snapshot einfügen.
3. Den Diff prüfen: geänderte Felder, nur in DataEngine vorhandene SKUs, nur im Live-Shop vorhandene SKUs und SKU-Duplikate.
4. Alternativ oder zusätzlich den CLI-Command `app:commerce:diff-ortho-vertrieb-products` mit `--json` für eine maschinelle Auswertung nutzen.
5. Erst wenn der Diff fachlich erklärt ist, darf eine separate Rückschreibpipeline geplant werden. Der Diff selbst aktiviert keinen Write-back.
Der Sync-Plan für den späteren Shop-Importer besteht immer aus CSV plus Manifest. Wenn `--target-file` gesetzt ist, schreibt die DataEngine zusätzlich `<target-file>.manifest.json` mit SHA-256, Zeilenzahl, Spaltenliste und Transaktionspflichten. Der genaue Importvertrag steht in `docs/ortho-shop-transactional-import-contract.md`.
### 5.8.2 Ortho-Vertrieb Google-Merchant-Feed erzeugen
Der Google-Merchant-Feed wird aus dem DataEngine Product Master erzeugt und nicht direkt aus dem Live-Shop.
Feed-URL:
```text
https://dataengine.domgan.de/feeds/google/ortho-vertrieb-products.tsv
```
Der technische Ablauf ist:
1. `ortho_product_master_to_google_merchant` installiert die Ortho-spezifische Export-Engine und Pipeline.
2. `commerce_pipeline_ortho_vertrieb_google_merchant` schreibt den Datapool `ortho_vertrieb_google_merchant_feed_v1`.
3. `app:commerce:export-ortho-google-merchant-feed` validiert die Zeilen und schreibt die tab-getrennte Feed-Datei.
4. `app:commerce:ortho-google-marketing-health` prüft nach dem Export kaufbare Produkte, URLs, Consent-/Tracking-Grundlagen und offene Google-Zugriffspunkte.
5. Der Compose-Service `scheduler` führt Export und Health-Check automatisch aus: einmal beim Containerstart und danach täglich um `23:45 Europe/Berlin`.
Produktive Ausführung:
```bash
php /srv/app/symfony/bin/console app:commerce:install-template ortho_product_master_to_google_merchant --tenant=default --overwrite --activate
php /srv/app/symfony/bin/console app:etl:plan pipeline commerce_pipeline_ortho_vertrieb_google_merchant --tenant=default
php /srv/app/symfony/bin/console app:commerce:export-ortho-google-merchant-feed --tenant=default
php /srv/app/symfony/bin/console app:commerce:ortho-google-marketing-health --json
```
Wichtig:
- Die Pipeline verändert nicht den Live-Shop `ortho-vertrieb.de`.
- Die Feed-Datei ist ein Laufzeitartefakt unter `var/public-feeds` und wird nicht in Git versioniert.
- Der Scheduler erzeugt die Datei vor dem Merchant-Center-Abruf um `00:00` neu, damit Product-Master-Änderungen täglich im Google-Feed landen.
- Die Scheduler-Übersicht unter `https://dataengine.domgan.de/scheduler` zeigt den nächsten Lauf, den letzten Export, Zeilen, Blocker, Warnungen, Marketing-Health und die relevanten Operator-Kommandos.
- Der Marketing-Health-Report liegt als JSON unter `https://dataengine.domgan.de/feeds/google/ortho-vertrieb-marketing-health.json` und als Markdown unter `https://dataengine.domgan.de/feeds/google/ortho-vertrieb-marketing-health.md`.
- Der Export blockiert leere Feeds und Pflichtfeldfehler.
- Landingpage-, Hauptbild- und zusätzliche Bild-URLs werden beim Export URL-sicher kodiert, damit Merchant Center nicht an Leerzeichen oder unkodierten Pfadzeichen scheitert.
- Für Ortho-Vertrieb-Bilder aus `/images/Orthomed-middle/` bevorzugt der Export automatisch die passende größere Datei aus `/images/Orthomed-big/`, wenn sie erreichbar ist. Die bisherige mittlere Datei bleibt dann als zusätzliche Bild-URL im Feed, damit Merchant Center eine robuste Bildalternative erhält.
- Produktkennzeichnungen werden getrennt gepflegt: `gtin` ist nur für echte GTIN/EAN/UPC/JAN/ISBN-Werte mit gültiger Prüfziffer, `mpn` ist die Herstellerartikelnummer, und `identifier_exists` steuert, ob Google eindeutige Produktkennzeichnungen erwarten soll.
- PZN, interne SKU oder MPN dürfen nicht in `gtin` kopiert werden. PZN kann als Label oder in einem künftigen fachlichen PZN-Feld geführt werden, ist aber kein GTIN-Ersatz für Google Merchant.
- Fehlende GTINs werden als Warnung gemeldet, solange Brand+MPN vorhanden sind. Echte GTIN/EAN-Werte dürfen nur aus belegten Quellen wie Verpackung, Herstellerdaten, GS1-Daten oder einem geprüften Lieferantenstamm nachgepflegt werden.
- Der produktive Google-Feed ist eine channel-spezifische Projektion, nicht der vollständige Product Master. Aktueller Stand: Der Feed enthält 33 Produkte. `MS10`/`Orthoaurinor` wird nicht exportiert, weil es nicht öffentlich aktiv sichtbar ist; `ORTH31`/`Ortholuna` wird nicht exportiert, weil Merchant Center dafür ein Gesundheits-/Nahrungsergänzungsmittel-Policy-Thema angezeigt hatte.
- Die Beschreibungen werden für den Google-Kanal neutral erzeugt und übernehmen keine ungeprüften Legacy-Wirkungs-, Heil- oder Therapieformulierungen.
- Nach einem sauberen Merchant-Center-Abruf können einzelne Produkte trotzdem noch auf "Wird überprüft" oder "Nicht genehmigt" stehen. Solche Restfälle sind Google-seitige Bildverarbeitung, Richtlinien- oder Produktprüfungen und müssen in Merchant Center diagnostiziert werden.
- Google Ads Kampagnen oder Werbeausgaben dürfen erst nach Merchant-Center-Freigabe, Konto-Verknüpfung, Conversion-Tracking-Prüfung und bestätigtem Budget gestartet werden. Aktueller Stand: Die bestehende Standard-Shopping-Kampagne `Domgan Google Shopping` ist mit `5,00 EUR/Tag` aktiviert; weitere Budget-, Gebots-, Zielgruppen- oder Kampagnenänderungen benötigen wieder eine explizite Freigabe.
Aktueller geprüfter Stand vom 2026-05-07:
- Merchant Center Datenquelle `PRODUCTS SOURCE 3` nutzt diese DataEngine-Feed-URL und hat die nachgeschärfte Datei um `09:21` mit `33` Produkten übernommen.
- Der Export meldet `0` Blocker und `33` GTIN-Hinweise.
- Alle aktuellen Feed-Produkt-, `srsltid`-, Bild- und Zusatzbild-URLs liefern mit Googlebot-User-Agent HTTP 200 und sind nicht per `X-Robots-Tag` blockiert.
- Merchant Center hat sich weiter verbessert: Nach der angeforderten Websiteprüfung ist `Produktseite nicht verfügbar` nicht mehr sichtbar. Übrig sind derzeit Google-seitige Bildverarbeitungshinweise (`Bild nicht verarbeitet` und `Bild kann nicht angezeigt werden`), die der Feed nun durch bevorzugte größere Bilddateien unterstützt und der Health-Report weiter überwacht.
- Die Datenquelle selbst meldet nach dem manuellen Abruf `Ihre Produkte wurden aktualisiert` und `Keine Probleme gefunden`; Produktfreigaben und Bildreview bleiben dennoch in der Produktdiagnose zu beobachten.
- Google Analytics 4 ist mit Google Ads `116-425-2507`, Merchant Center `119414541` und Search Console `ortho-vertrieb.de` verknüpft. Die Search Console Domain-Property ist verifiziert, und die HTTPS-Sitemap `https://www.ortho-vertrieb.de/sitemap.xml` ist erfolgreich eingereicht.
- Google Ads API ist weiterhin durch `DEVELOPER_TOKEN_NOT_APPROVED` blockiert. Bis Basic/Standard Access oder AgentGuard-Providerzugriff verfügbar ist, bleiben echte Google-Ads-, GA4-, Search-Console- und Merchant-Diagnosemetriken browsergestützte Prüfschritte oder manuelle Exporte.
Die vollständige Rollout-Checkliste steht in `docs/google-merchant-rollout.md`.
### 5.2 Pipeline ausführen
Pipelines können direkt gestartet werden über:
1. Runtime-Katalog auf der Startseite
2. Managed-Definitions-Workbench, wenn die Definition `active` ist
3. Managed-Definition-Detailseite, wenn die Definition `active` ist
4. Legacy-YAML-Browser oder Legacy-Detailseite, wenn die YAML-Datei gültig parsebar ist
Ausführung:
1. Pipeline-Karte oder Tabellenzeile wählen
2. `Pipeline ausführen`, `Run` oder `Run starten` klicken
3. Das System legt einen Run in PostgreSQL an
4. Symfony plant die Ausfuehrung nativ und entscheidet zwischen `native_staging_pipeline` und Legacy-Bridge
5. Wenn alle Schritte nativen Import oder nativen Unitize nutzen, laeuft die Pipeline direkt als `native_staging_pipeline`
6. Nur falls kein nativer Pipeline-Pfad moeglich ist, wird fuer Managed Definitions ein isolierter Overlay-Snapshot fuer die Legacy-Bridge vorbereitet
7. Der Run wird nativ oder ueber die Legacy-Bridge abgearbeitet
8. Im Run-Detail ist der native Preflight mit Strategie, Quelle und Schritten sichtbar
### 5.3 Engine ausführen
Engines können direkt gestartet werden über:
1. Runtime-Katalog auf der Startseite
2. Managed-Definitions-Workbench, wenn die Definition `active` ist
3. Managed-Definition-Detailseite, wenn die Definition `active` ist
4. Legacy-YAML-Browser oder Legacy-Detailseite, wenn die YAML-Datei gültig parsebar ist
Ausführung:
1. Engine-Karte oder Tabellenzeile wählen
2. `Engine ausführen`, `Run` oder `Run starten` klicken
3. Der Run wird in PostgreSQL protokolliert
4. Symfony plant die Ausfuehrung nativ und entscheidet zwischen `native_import_file_to_staging` und Legacy-Bridge
5. Native Managed File-Importe schreiben direkt in tenant-isolierte PostgreSQL-Staging-Datapools
6. Native Managed Unitize-Engines lesen aus vorhandenen nativen Staging-Datapools und schreiben ihr Ziel ebenfalls dorthin
7. Standalone-Unitize braucht deshalb einen vorhandenen nativen Source-Datapool oder muss ueber eine passende Pipeline zusammen mit dem Import laufen
8. Nur nicht-native Engines erhalten bei Bedarf einen isolierten Overlay-Snapshot fuer die Legacy-Bridge
### 5.4 Run-Detail lesen
Im Run-Detail werden angezeigt:
- Run-Status
- Runner-Typ und Name
- Trigger-Quelle
- Start-/Endzeit
- Exit-Code
- stdout/stderr
- nativer Ausfuehrungsplan mit Quelle, Schritten, Connections und Validierungsfehlern
- aktive Strategie der Ausfuehrung, inklusive Hinweis auf native PostgreSQL-Staging-Runtime
- Anzahl und Herkunft von Managed-Definition-Overlays
- gebuendelte Prozessausgaben fuer stdout und stderr in einer gemeinsamen Diagnoseansicht
- dieselbe Top-Bar-Navigation mit direkten Sprüngen zu Metadaten, Ausführungsplan und Prozessausgaben
### 5.9 Infrastruktur bedienen
Unter `https://dataengine.domgan.de/infrastructure` liegt die operative Infrastrukturansicht der DataEngine.
Sie bündelt zwei getrennte Betriebsbereiche:
- Connection-Profile als tenant-isolierte Verbindungsmetadaten
- native PostgreSQL-Staging-Datapools der Managed Runtime
Die Seite bietet:
- KPI-Überblick über Profilbestand, Kategorien und Staging-Datapools
- eine Coverage-Ansicht für fehlende und als Draft vorbereitete Connection-Profile mit Verwendungszählern, Runner-Typen, Bulk-Scaffold und Direktlink in den Profil-Editor
- gruppierte Listen aller vorhandenen Connection-Profile
- Detail- und Pflegeansicht pro Connection-Profil
- Anlegen neuer Profile innerhalb derselben Shell
- Bestandsliste nativer Staging-Datapools mit direktem Sprung in die Detailansicht
Wichtig für die Provisionierung:
- Connection-Profile besitzen jetzt einen expliziten Status `draft` oder `ready`.
- `draft` bedeutet: Profil ist im Tenant inventarisiert und bearbeitbar, blockiert Planner und Runtime aber weiterhin bewusst.
- `ready` bedeutet: Profil darf von Planner und Runtime als wirksam behandelt werden.
- Über die Aktion `Alle fehlenden Drafts anlegen` wird der aktuelle Provisioning-Backlog als bearbeitbarer Draft-Bestand in PostgreSQL überführt, ohne externe Systeme zu kontaktieren.
In den Profil-Detailseiten können gepflegt werden:
- Kategorie
- Profilname
- Transport
- Provisioning-Status
- Source Path
- `settings` als JSON-Objekt
- Secret-Felder als zeilenweise Liste
In den Datapool-Detailseiten sichtbar sind:
- Name, Runner, Herkunft und Zeitstempel
- Spaltenschema
- Zeilenstichprobe mit bis zu 25 Zeilen
- Vollansicht kleiner Datapools bis 200 Zeilen
- gespeicherte Datapool-Metadaten als JSON
Wichtig:
- Connection-Profile bleiben Betriebsdaten des Tools und werden nicht zurück in Legacy-Dateien geschrieben.
- Wenn serverlokale Legacy-Credentials fehlen, zeigt die Coverage-Ansicht die offenen Profilnamen und ihre konkrete Nutzung im Katalog.
- Große Datapools werden absichtlich nicht standardmäßig vollständig gerendert.
- Die Übersicht im Dashboard bleibt als Schnellzugriff erhalten; detaillierte Pflege liegt jetzt in der Infrastrukturansicht.
### 5.9 Legacy YAML Browser
Unter `https://dataengine.domgan.de/legacy-definitions` liegt die read-only Sicht auf den bisherigen YAML-Bestand im Filesystem.
Der Legacy-Browser und seine Detailseiten laufen inzwischen ebenfalls im neuen Shell-System mit Bereichsankern für Überblick, Pipelines, Engines, Raw YAML, Metadaten und gegebenenfalls Pipeline-Schritte.
Die Ansicht bietet:
- Liste aller gefundenen Legacy-Pipelines
- Liste aller Legacy-Engines je Runner-Typ
- Parse-Status pro Datei
- Dateipfad der Originaldatei
- erkannte Feature-Flags und Connection-Hinweise
- Raw-YAML-Ansicht pro Definition
- Anzeige, ob bereits eine passende Managed Definition existiert
- sicheren Import als tenant-isolierter `Draft` in die Workbench
- direkte Run-Buttons für gültig parsebare Legacy-Pipelines und Legacy-Engines
Wichtig:
- Der Browser bearbeitet keine Legacy-Dateien direkt.
- Die Originaldateien im Filesystem bleiben unveraendert.
- Ungültige Legacy-YAML-Dateien sind sichtbar, aber nicht direkt lauffähig.
- Bearbeitet wird erst nach dem Import in die Managed-Definitions-Workbench.
- Wenn fuer dieselbe Definition bereits eine Managed Definition existiert, wird diese geoeffnet statt eine Duplikatkopie anzulegen.
Empfohlener Workflow:
1. Legacy-Definition im Browser suchen.
2. Detailansicht oeffnen und Raw-YAML sowie Metadaten pruefen.
3. `Als Managed Draft importieren` ausloesen.
4. In der Workbench weiter bearbeiten, validieren und aktivieren.
## 6. MCP verwenden
Der Endpunkt `POST https://dataengine.domgan.de/mcp` spricht JSON-RPC im MCP-Stil.
Daneben gibt es eine öffentliche Discovery-Schicht für Crawler und AI-Clients:
- `GET https://dataengine.domgan.de/llms.txt`
- `GET https://dataengine.domgan.de/site-context.md`
- `GET https://dataengine.domgan.de/ai-discovery.md`
- `GET https://dataengine.domgan.de/manual.md`
- `GET https://dataengine.domgan.de/commerce-templates`
Diese Dokumente sind bewusst read-only, enthalten keine tenant-spezifischen Betriebsdaten und verlinken auf denselben kanonischen MCP-Endpunkt der Plattform.
Unterstuetzt werden aktuell mindestens:
- `initialize`
- `ping`
- `tools/list`
- `tools/call`
- `resources/list`
- `resources/read`
- `prompts/list`
Wichtige Tools:
- `tenant_list`, `tenant_create`
- `runtime_catalog_scan`
- `managed_definition_list`, `managed_definition_get`, `managed_definition_create`, `managed_definition_update`, `managed_definition_delete`, `managed_definition_revisions`
- `legacy_definition_list`, `legacy_definition_get`, `legacy_definition_import`
- `connection_profile_list`, `connection_profile_coverage`, `connection_profile_get`, `connection_profile_upsert`, `connection_profile_delete`, `connection_profile_scaffold_missing`
- `execution_plan`, `execution_start`
- `run_list`, `run_get`
- `native_staging_datapool_list`, `native_staging_datapool_get`
- `commerce_template_list`, `commerce_template_get`, `commerce_template_install_recipe`
Wichtig:
- Legacy-Dateien bleiben auch ueber MCP read-only.
- Schreibende Aenderungen laufen ueber Managed Definitions und andere tenant-eigene PostgreSQL-Daten.
- Managed-Definitions-Tools akzeptieren entweder `yaml_content` oder ein strukturiertes `document`.
- Dadurch lassen sich auch `Mapping`, `Filters`, `Source_Files`, `Datapool_Definition` und andere tiefe YAML-Abschnitte per MCP bearbeiten.
### 6.1 Beispiel: MCP initialisieren
```json
{
"jsonrpc": "2.0",
"id": 1,
"method": "initialize",
"params": {}
}
```
### 6.2 Beispiel: Tools auflisten
```json
{
"jsonrpc": "2.0",
"id": 2,
"method": "tools/list",
"params": {}
}
```
### 6.3 Beispiel: Managed Definition per strukturiertem Dokument anlegen
```json
{
"jsonrpc": "2.0",
"id": 3,
"method": "tools/call",
"params": {
"name": "managed_definition_create",
"arguments": {
"tenant": "default",
"definition_kind": "engine",
"runner_type": "import",
"name": "mcp_sample_import",
"title": "MCP Sample Import",
"status": "draft",
"document": {
"Source_Connection_Type": "file",
"Target_Config": "importEngine",
"Engine_Mode": "full",
"Source_Files": [
{
"Full_Path": "/srv/app/docs/examples/datapool-import.example.csv",
"Datatype": "csv",
"Headline": "yes",
"Field_Termination": ";",
"Field_Enclosure": "\"",
"Line_Termination": "\\n",
"Target_Datapool": "mcp_sample_import_pool"
}
]
}
}
}
}
```
### 6.4 Beispiel: Pipeline planen
```json
{
"jsonrpc": "2.0",
"id": 4,
"method": "tools/call",
"params": {
"name": "execution_plan",
"arguments": {
"tenant": "default",
"runner_type": "pipeline",
"name": "pipeline_update_articles_wandmontage",
"arguments": []
}
}
}
```
### 6.5 Beispiel: Pipeline oder Engine starten
```json
{
"jsonrpc": "2.0",
"id": 5,
"method": "tools/call",
"params": {
"name": "execution_start",
"arguments": {
"tenant": "default",
"runner_type": "pipeline",
"name": "pipeline_update_articles_wandmontage",
"mode": "queue",
"arguments": []
}
}
}
```
## 7. Codex Plugin verwenden
Das Plugin `plugins/dataengine-mcp` kapselt denselben Endpunkt fuer Codex.
Ziel:
- Nutzung dieses DataEngine-MCP-Servers aus anderen Codex-Projekten
- versionierte Plugin-Definition direkt im privaten Repository
- keine globale Systemaenderung ausserhalb dieses Projekts in diesem Schritt
Die relevante Datei ist:
- `plugins/dataengine-mcp/.mcp.json`
Dort zeigt der konfigurierte MCP-Server `dataengine` auf:
- `https://dataengine.domgan.de/mcp`
Wenn ein anderes Codex-Projekt dieses Plugin referenziert oder installiert, kann es darueber direkt auf die DataEngine-Funktionen zugreifen.
### 5.9 Native Staging Runtime
Die native Staging-Runtime ist die erste vollstaendig neue fachliche Laufzeit im Symfony-Tool.
Aktuell unterstuetzt:
- Managed Import-Engines vom Typ `import`
- Managed Transform-Engines vom Typ `unitize`, `enhance` und `export`
- Dateiquellen `csv`, `json`, `yaml`
- Ziel `native_runtime`, `native_staging` oder der Legacy-Alias `importEngine`
- Managed Pipelines, deren Schritte vollstaendig aus nativ unterstuetzten Import- und Transform-Engines bestehen
Verhalten:
- die Quelldateien werden im Dashboard-Container gelesen
- die Daten werden tenant-isoliert in PostgreSQL gespeichert
- native Transform-Runs lesen aus vorhandenen nativen Staging-Datapools und erzeugen neue tenant-isolierte Zieldatapools
- Staging-Datapools werden pro `tenant + datapool_name` serialisiert ersetzt, damit parallele Trigger keine Unique-Constraint-Fehler erzeugen
- Dashboard, CLI und Worker verwenden dafuer dieselbe Symfony-Laufzeit
- fuer Query-basierte Transform-Pfade wird intern eine lokale SQLite-Runtime gegen die PostgreSQL-Staging-Datapools aufgebaut; dadurch bleiben Tests und Ausfuehrung innerhalb der Tool-DB
Aktuell unterstuetzte native Transform-Teilmenge:
- `Source_Config` und `Target_Config` duerfen `native_runtime`, `native_staging` oder fuer den Legacy-kompatiblen Managed-Pfad `importEngine`, `unitizeEngine`, `enhanceEngine` beziehungsweise `exportEngine` sein
- `Source_Datapool`, `Datapool` oder `Target_Datapool`, `Mapping` und optional `Datapool_Definition` werden ausgewertet
- direkte Mapping-Werte wie `source_pool.field`, `field` und Literale wie `(foo)` werden unterstuetzt
- generierte Query-Ausfuehrung fuer `Joins`, `Left_Join`, einfache `Read_Conditions` und Ausdrucks-Mappings wie `CONCAT(...) AS alias` wird unterstuetzt
- freie `Sql`-Bloecke werden in einer nativen Teilmenge unterstuetzt, aktuell vor allem `SELECT`, `UNION`, `GROUP_CONCAT`, `CONCAT`, `COALESCE`, `SUBSTRING`, `IF`, `MAX`, `TRIM` sowie MySQL-Trim-Modifikatoren wie `TRIM(LEADING ...)`, `TRIM(TRAILING ...)` und `TRIM(BOTH ... FROM ...)` gegen native Staging-Datapools
- `Field_Adapter` wird nativ in einer wachsenden Teilmenge ausgefuehrt, derzeit unter anderem `replace`, `replace_empty`, `replace_empty_else`, `replace_greater`, `replace_smaller`, `replace_not_equals`, `trim`, `ltrim`, `rtrim`, `substr`, `get_preg_match`, `explode`, `tolower`, `round`, `float_pricision`, `str_pad`, `simple_multiplication`, `simple_division`, `select_max_field`, `concat_fields`, `getfromjsonfield`, `absolutevalue`, `source`, `concat`, `concat_source`, `extend_if_not_empty`, `add_string`, `add_prefix`, `addslashes`, `change_charset`, `nl2br`, `strtotime`, `md5` und `if`
- `Filters` werden nativ in einer wachsenden Teilmenge ausgewertet, derzeit stringbasierte Bedingungen, `Equals`, `In_String`, `In_Pool_Filter_Adapter` und `Not_In_Pool_Filter_Adapter` auf nativen Staging-Datapools
- `Cross_Field_Adapter`, `Cross_Field_Adapters` und `Cross_Field_Adapters_Target_Injection` werden nativ in einer ersten Staging-Teilmenge unterstuetzt, derzeit fuer native PostgreSQL-Staging-Datapools mit einfacher Lookup-Form, Mapping-Lookups und den Optionen `keep_existing`, `concat`, `match_only`, `match_not_empty_only` und `case_sensitiv`
- passive Legacy-Metadaten wie `In_Export` werden fuer die native Ausfuehrung toleriert und nicht als Blocker gewertet
- Query-Modi sind `direct`, `generated` und `sql`; sie erscheinen im Ergebnis- und Datapool-Metadatenbild
- `Cross_Dataset_Filter_Adapter` wird nativ in einer ersten benannten Teilmenge unterstuetzt, derzeit fuer Duplicate-Key-, No-Picture-, Slow-Delivery-, Marketplace-, Non-Parent- und Variant-Without-Parent-Filter auf nativen PostgreSQL-Staging-Datapools
- `File_Field_Adapter`, Legacy-Alias `File_Field_Adapters` und `File_Field_Adapter_Source_Injection` werden nativ in einer sicheren Teilmenge unterstuetzt: erlaubt sind lokale Legacy-Adapterdateien pro Runner-Typ, target- und source-injection, verkettete `Files`, lokal sichere row-mutating In-Memory-Semantik ueber `cross_dataset_buffer` beziehungsweise `dataset_amount` sowie kontrolliertes Additional-Datapool-Loading ueber interne Legacy-Aliasse plus `Datapool`, `Identifier`, `Fields` und einfache skalare `Conditions` auf nativen Staging-Datapools sowie read-only Helper-Lookups ueber verifizierbare lokale Legacy-Datenbankprofile in `mysql` oder `pgsql`, solange keine Datei-, Netzwerk- oder Prozess-Nebenwirkungen benoetigt werden
- nicht unterstuetzte SQL-Konstrukte sowie weitere Legacy-Sondersemantik bleiben vorerst im Legacy-Pfad
Wichtig fuer die native Pool-Filter:
- `In_Pool_Filter_Adapter` und `Not_In_Pool_Filter_Adapter` arbeiten derzeit nur gegen native PostgreSQL-Staging-Datapools innerhalb des Tools
- die Mapping-Variante von `Not_In_Pool_Filter_Adapter` behaelt bewusst die Legacy-Semantik bei, bei der jede nicht passende Pool-Zeile als Filtertreffer zaehlt
- dadurch koennen mehrere Filtergruende fuer eine einzelne Datensatzzeile entstehen; diese werden in den Datapool-Metadaten mitgezaehlt
Wichtig fuer die native Cross-Field-Adapter:
- die native Cross-Field-Teilmenge arbeitet derzeit nur gegen native PostgreSQL-Staging-Datapools innerhalb des Tools
- die einfache Legacy-Form mit `Local_Key` und `Cross_Key` behaelt bewusst das beobachtete Legacy-Verhalten bei: die aktuelle Zeile wird fuer den Lookup ueber den Wert von `Cross_Key` adressiert
- Mapping-basierte Cross-Lookups nutzen ihre explizite lokale Feldzuordnung und koennen auf Wunsch case-insensitiv arbeiten
- `Cross_Field_Adapters_Target_Injection` liest gegen den bereits aufgebauten Ziel-Datensatz derselben Zeile; dadurch muss das referenzierte Zielfeld vor dem injizierenden Mapping aufgebaut werden
Wichtig fuer die native Cross-Dataset-Filter:
- die native Cross-Dataset-Teilmenge arbeitet derzeit nur gegen native PostgreSQL-Staging-Datapools innerhalb des Tools
- unterstuetzt sind aktuell erste benannte Legacy-Adapter fuer Duplicate-Key-, No-Picture-, Slow-Delivery-, Marketplace-, Non-Parent- und Variant-Without-Parent-Semantik
- die nativen Filter behalten bewusst die beobachtete Legacy-State-Semantik bei: ein Engine-Lauf teilt einen gemeinsamen Cross-Dataset-Zustand ueber alle Quellzeilen und unterstuetzten Cross-Dataset-Filter
- row-mutierende Legacy-Sonderfaelle auf verworfenen Zeilen sind noch nicht Teil der nativen Teilmenge und bleiben bis zur spaeteren Portierung im Legacy-Pfad
Wichtig fuer die native File-Field-Adapter:
- die native File-Field-Teilmenge arbeitet derzeit nur fuer Managed Transform-Engines auf nativen PostgreSQL-Staging-Datapools innerhalb des Tools
- unterstuetzt sind `File_Field_Adapter`, Legacy-Alias `File_Field_Adapters` und `File_Field_Adapter_Source_Injection`, wenn die referenzierte Legacy-PHP-Datei im runner-spezifischen Adapterordner liegt und innerhalb der sicheren nativen Teilmenge bleibt
- `Files`-Ketten werden in derselben Reihenfolge ausgefuehrt wie im YAML
- `File_Field_Adapters` wird dabei bewusst als Legacy-Kompatibilitaetsalias derselben target-seitigen Adaptersemantik behandelt wie `File_Field_Adapter`
- wenn `File` fehlt, wird wie im Legacy-Bestand die Default-Aufloesung `Target_Datapool.Zielfeld` verwendet
- Additional-Datapool-Loading ist nativ erlaubt, wenn die Definition interne Runtime-Aliasse nutzt und `Datapool`, `Identifier` sowie `Fields` auf einen nativen Staging-Datapool innerhalb des Tools verweisen
- zusaetzlich sind fuer diesen internen Helper-Pfad einfache skalare SQL-aehnliche `Conditions` erlaubt, zum Beispiel `active = 1`; die Bedingung wird vor dem Adapter-Aufruf auf demselben nativen Staging-Datapool ausgewertet
- alternativ sind read-only Helper-Lookups ueber `Config` oder `Engine` erlaubt, wenn das referenzierte lokale Legacy-Datenbankprofil in Symfony als `native_ready` verifizierbar ist; aktuell ist dieser Pfad fuer `mysql` und `pgsql` vorgesehen
- wenn fuer den konfigurierten `Identifier` kein passender Wert in der aktuellen Quell- oder Zielzeile gefunden wird, wird wie im Legacy-Pfad die vollstaendige geladene Additional-Datapool-Collection an den Adapter uebergeben
- ebenfalls verifiziert ist eine erste row-mutating Teilmenge, bei der der Legacy-Adapter nur den nativen In-Memory-Status innerhalb eines Engine-Runs veraendert; bestaetigt wurde das lokal unter anderem mit `export_articles_oms.attr22`
- bewusst nicht nativ unterstuetzt sind aktuell komplexe oder nicht-skalare `Conditions`, nicht native-ready externe `Config`- oder `Engine`-Profile, ODBC-basierte Helper-Profile sowie Adapterdateien mit Datei-, Netzwerk-, Prozess- oder sonstigen Seiteneffekten
- solche Definitionen bleiben planbar, fallen aber fuer die fachliche Ausfuehrung weiterhin auf den Legacy-Pfad zurueck
### 5.10 YAML-Kompatibilitaetsansicht
Unter `https://dataengine.domgan.de/compatibility` wird die Kompatibilitaet der bestehenden YAML-Dateien explizit ausgewertet.
Die Seite nutzt nun dieselbe Shell-Struktur und führt über `Mandantenkontext`, `Vergleich` und `Blocker` durch die Auswertung.
Die Ansicht trennt zwei Perspektiven:
- `active`
tenant-spezifische Runtime-Sicht; gleichnamige aktive Managed Definitions koennen hier Legacy-YAMLs uebersteuern
- `legacy-filesystem`
direkter Blick auf die vorhandenen Legacy-YAML-Dateien ohne Managed-Overrides
Damit gilt fuer die bestehende Prozesssteuerung:
- Das aktuelle YAML-Format bleibt erhalten und soll auch im Zielsystem weiterhin funktionieren.
- Nicht alles muss dafuer sofort nativ in Symfony laufen.
- Solange eine Definition fachlich korrekt ist, darf sie weiterhin ueber die Legacy-Bridge ausgefuehrt werden.
- Erst wenn ein nativer Symfony-Pfad verfuegbar ist, wird dieselbe Definition tenant-spezifisch nativ geplant oder ausgefuehrt.
- Neue Managed Definitions duerfen fuer native Import- und Transform-Pfade weiterhin die vertrauten Legacy-Konfigurationswerte `importEngine`, `unitizeEngine`, `enhanceEngine` und `exportEngine` verwenden.
- Blockierte YAMLs sind keine Migrationsentscheidung, sondern echte Inkonsistenzen wie fehlende referenzierte Engines oder Connections.
## 6. Wichtige CLI-Commands
Alle Commands laufen im Dashboard-Container.
Beispiel:
```bash
ssh ortho-live 'cd /opt/containers/drive-dataengine && docker compose -f compose.yaml exec -T dashboard php /srv/app/symfony/bin/console <command>'
```
### 6.1 Katalog und Planung
- `app:etl:list [--tenant=default]`
listet den tenant-spezifischen Runtime-Katalog aus Managed Definitions und Legacy-Definitionen
- `app:etl:plan <runner-type> <name> [--tenant=default]`
erzeugt den nativen Ausfuehrungsplan inklusive Definitionsquelle und Managed-Overlay-Zusammenfassung
- `app:etl:report-yaml-compatibility [--tenant=default] [--source=legacy|managed|all] [--scope=active|legacy-filesystem] [--format=table|json]`
bewertet die YAML-Kompatibilitaet entweder fuer den aktiven Runtime-Katalog oder direkt fuer den rohen Legacy-Dateibestand
- `app:commerce:templates [--json]`
listet den Commerce-Template-Katalog, die Exportkanäle und installierbaren Recipes
- `app:commerce:install-template <recipe> [--tenant=default] [--overwrite] [--json]`
importiert eine Commerce-Recipe als tenant-isolierte Managed Drafts
- `app:commerce:install-template <recipe> [--tenant=default] [--overwrite] [--activate] [--json]`
importiert eine Commerce-Recipe explizit als aktive Managed Definitions; nur für geprüfte operative Recipes verwenden
- `app:commerce:bootstrap-erp-from-product-master [--tenant=default] [--source-datapool=ortho_vertrieb_product_master_v1] [--erp-base-url=https://erp.domgan.de] [--erp-tenant=ortho-vertrieb] [--sync-stock] [--limit=0] [--commit] [--json]`
initialisiert den ERP-Tenant aus einem geprüften DataEngine Product Master; ohne `--commit` ist es ein Dry-Run
- `app:commerce:import-erp-products <products-json-or-url> [--stock-source=<stock-json-or-url>] [--tenant=default] [--erp-tenant=default] [--datapool=commerce_product_master_v2] [--min-products=0] [--commit] [--json]`
importiert ERP-Produkte und Bestände in den kanonischen Product Master v2
- `app:commerce:load-channel-overrides <overrides-json|-> [--tenant=default] [--datapool=commerce_channel_overrides_v1] [--commit] [--json]`
lädt channel-spezifische Override-Regeln in die DataEngine
- `app:commerce:project-channel <channel> [--tenant=default] [--source-datapool=commerce_product_master_v2] [--override-datapool=commerce_channel_overrides_v1] [--commit] [--json]`
erzeugt einen kanalbezogenen Projektions-Datapool aus ERP-Master und Overrides
- `app:commerce:build-repricing-plan [<repricing-json|->] [--channel=ortho_shop] [--tenant=default] [--source-datapool=commerce_product_master_v2] [--commit] [--json]`
prüft ecommerceOS-Preisempfehlungen gegen Kostenbasis und Mindestmargen
- `app:commerce:export-ortho-shop-sync-plan [--tenant=default] [--source-datapool=commerce_channel_ortho_shop_products_v1] [--price-plan-datapool=commerce_repricing_plan_v1] [--target-file=<csv>] [--commit] [--json]`
erzeugt den Ortho-Shop-Importplan als DataEngine-Datapool und optional als CSV plus `<csv>.manifest.json`
- `app:commerce:import-ortho-vertrieb-products <snapshot-file|-> [--tenant=default] [--datapool=ortho_vertrieb_product_master_v1] [--include-inactive] [--commit] [--json]`
prüft oder importiert einen read-only Ortho-Vertrieb Produkt-Snapshot in den Commerce Product Master Datapool
- `app:commerce:diff-ortho-vertrieb-products <snapshot-file|-> [--tenant=default] [--datapool=ortho_vertrieb_product_master_v1] [--include-inactive] [--field=<feld>] [--max-details=50] [--json]`
vergleicht den Product Master read-only mit einem frischen Ortho-Vertrieb Live-Shop-Snapshot und schreibt weder in den Shop noch in den Product Master
- `app:commerce:export-ortho-google-merchant-feed [--tenant=default] [--pipeline=commerce_pipeline_ortho_vertrieb_google_merchant] [--datapool=ortho_vertrieb_google_merchant_feed_v1] [--skip-run] [--json]`
führt die Ortho-Vertrieb Google-Merchant-Pipeline aus, validiert den Feed und schreibt die TSV-Datei für Merchant Center Scheduled Fetch
- `app:commerce:ortho-google-marketing-health [--check-urls] [--json]`
prüft den aktuellen Ortho-Vertrieb Google-Merchant-Feed, Product-/Image-URLs, Tracking-Grundlagen und dokumentiert Google-/API-Gates als JSON und Markdown
Beispiel:
```bash
php /srv/app/symfony/bin/console app:etl:list --tenant=default
php /srv/app/symfony/bin/console app:etl:plan pipeline pipeline_update_articles_wandmontage --tenant=default
php /srv/app/symfony/bin/console app:etl:report-yaml-compatibility --tenant=default --source=legacy --scope=legacy-filesystem --format=table
php /srv/app/symfony/bin/console app:commerce:templates
php /srv/app/symfony/bin/console app:commerce:install-template csv_to_google_merchant --tenant=default
php /srv/app/symfony/bin/console app:commerce:install-template ortho_product_master_to_google_merchant --tenant=default --overwrite --activate
php /srv/app/symfony/bin/console app:commerce:import-erp-products docs/examples/erp-products.example.json --stock-source=docs/examples/erp-stock.example.json --tenant=default --commit
php /srv/app/symfony/bin/console app:commerce:load-channel-overrides docs/examples/channel-overrides.example.json --tenant=default --commit
php /srv/app/symfony/bin/console app:commerce:project-channel ortho_shop --tenant=default --commit
php /srv/app/symfony/bin/console app:commerce:build-repricing-plan docs/examples/repricing-snapshot.example.json --channel=ortho_shop --tenant=default --commit
php /srv/app/symfony/bin/console app:commerce:export-ortho-shop-sync-plan --tenant=default --commit --target-file=/srv/app/symfony/public/feeds/ortho-shop/ortho-vertrieb-shop-sync.csv
php /srv/app/symfony/bin/console app:commerce:import-ortho-vertrieb-products /tmp/ortho-products.json --tenant=default
php /srv/app/symfony/bin/console app:commerce:import-ortho-vertrieb-products /tmp/ortho-products.json --tenant=default --commit
php /srv/app/symfony/bin/console app:commerce:diff-ortho-vertrieb-products /tmp/ortho-products.json --tenant=default
php /srv/app/symfony/bin/console app:commerce:diff-ortho-vertrieb-products /tmp/ortho-products.json --tenant=default --json
php /srv/app/symfony/bin/console app:commerce:export-ortho-google-merchant-feed --tenant=default
php /srv/app/symfony/bin/console app:commerce:ortho-google-marketing-health --json
```
### 6.2 Ausfuehrung
- `app:etl:run-pipeline <name> [--tenant=default] [--async]`
- `app:etl:run-engine <runner-type> <name> [--tenant=default] [--async]`
fuehrt je nach nativem Plan direkt in Symfony oder ueber die Legacy-Bridge aus
- `app:execution:run <run-id> [--tenant=default]`
interner Worker-Command
### 6.3 Verbindungsprofile
- `app:etl:list-connections`
zeigt tenant-wirksame Verbindungsprofile inklusive `draft`- und `ready`-Status
- `app:legacy:sync-connections --tenant=default`
synchronisiert redigierte Metadaten tenant-sicher nach PostgreSQL
- `app:etl:report-missing-connections [--tenant=default] [--source=all|legacy|managed] [--format=table|json] [--limit=50]`
aggregiert alle referenzierten Connection-Profile über Engines und Pipelines, trennt zwischen `ready`, `draft` und `missing`, und zeigt Verwendungsbeispiele
- `app:etl:scaffold-missing-connections [--tenant=default] [--source=all|legacy|managed] [--name=<profil>] [--limit=<n>] [--format=table|json]`
legt fehlende Connection-Profile als tenant-isolierte Drafts an, ohne sie auf `ready` zu setzen
- `app:etl:report-missing-connection-profiles [--tenant=default] [--runner-type=push --runner-type=trigger] [--scope=legacy-filesystem|active] [--source=legacy|managed|all] [--format=table|json]`
fokussiert die ältere Detailauswertung auf blockierte Legacy-Runner und gruppiert ungelöste Connection-Profile, also fehlende oder nur als Draft vorhandene Profile
### 6.4 Native Datenbank-Runtime
- `app:etl:list-database-runtime`
zeigt, welche Legacy-Datenbankprofile in Symfony nativ nutzbar sind
- `app:etl:test-database-connection <profile>`
fuehrt einen read-only Verbindungsprobe-Lauf fuer ein nativen `mysql`- oder `pgsql`-Profil aus
- `app:etl:list-datapools <profile>`
listet Datapools eines nativen MySQL-Profils read-only auf
- `app:etl:inspect-datapool <profile> <table> [--with-count] [--show-create-sql]`
zeigt Spaltenstruktur eines Datapools und optional Zeilenzahl bzw. `SHOW CREATE TABLE`
### 6.5 Native Datapool-Write-Operationen
- `app:etl:drop-datapool <profile> <table>`
fuehrt `DROP TABLE IF EXISTS` kontrolliert fuer ein natives MySQL-Profil aus
- `app:etl:truncate-datapool <profile> <table>`
fuehrt `TRUNCATE TABLE` kontrolliert fuer ein natives MySQL-Profil aus
- `app:etl:create-datapool-definition <profile> <table> <definition-file> [--drop-existing]`
erzeugt einen Datapool aus einer YAML- oder JSON-Blueprint-Datei
- `app:etl:create-datapool-csv <profile> <table> <csv-file> [--drop-existing] [--load-after-create]`
erzeugt einen Datapool aus der CSV-Kopfzeile und laedt optional direkt die CSV-Daten
- `app:etl:create-datapool-data <profile> <table> <data-file> [--drop-existing]`
leitet einen Datapool aus JSON- oder YAML-Daten ab
- `app:etl:load-datapool-data <profile> <table> <data-file> [--truncate-before-load]`
laedt JSON- oder YAML-Daten nativ per Multi-Insert in einen bestehenden MySQL-Datapool
- `app:etl:copy-datapool-structure <profile> <source-table> <target-table> [--drop-existing]`
kopiert die Struktur eines bestehenden MySQL-Datapools auf eine neue Tabelle
- `app:etl:load-datapool-csv <profile> <table> <csv-file>`
fuehrt nativ `LOAD DATA LOCAL INFILE` fuer einen bestehenden MySQL-Datapool aus
Beispiele:
```bash
php /srv/app/symfony/bin/console app:etl:drop-datapool importEngine temp_articles
php /srv/app/symfony/bin/console app:etl:truncate-datapool importEngine temp_articles
php /srv/app/symfony/bin/console app:etl:create-datapool-definition importEngine temp_articles /srv/app/docs/examples/datapool-definition.example.yaml --drop-existing
php /srv/app/symfony/bin/console app:etl:create-datapool-csv importEngine temp_articles /srv/app/docs/examples/datapool-import.example.csv --drop-existing --load-after-create
php /srv/app/symfony/bin/console app:etl:create-datapool-data importEngine temp_articles /srv/app/docs/examples/datapool-data.example.json --drop-existing --load-after-create
php /srv/app/symfony/bin/console app:etl:load-datapool-data importEngine temp_articles /srv/app/docs/examples/datapool-data.example.json --truncate-before-load
php /srv/app/symfony/bin/console app:etl:copy-datapool-structure importEngine articles_source articles_target --drop-existing
php /srv/app/symfony/bin/console app:etl:load-datapool-csv importEngine temp_articles /srv/app/docs/examples/datapool-import.example.csv
```
Blueprint-Format:
- `columns`
Pflichtfeld, Mapping aus Spaltenname auf MySQL-Spaltendefinition
- `primary_key`
optionale Liste von Spalten fuer den Primary Key
- `indices`
optionale Liste von Indexdefinitionen, einzelne Werte duerfen auch `firma,ordernumber` enthalten
- `engine`
optional, Standard `INNODB`
- `charset`
optional, Standard `latin1`
- `add_filter_reason`
optional, fuegt die Spalte `filter_reason MEDIUMTEXT` hinzu, falls sie noch fehlt
Ein lauffaehiges Beispiel liegt unter:
- `docs/examples/datapool-definition.example.yaml`
- `docs/examples/datapool-import.example.csv`
- `docs/examples/datapool-data.example.json`
Beispiele:
```bash
php /srv/app/symfony/bin/console app:etl:list-database-runtime
php /srv/app/symfony/bin/console app:etl:test-database-connection domgan
php /srv/app/symfony/bin/console app:etl:list-datapools importEngine
php /srv/app/symfony/bin/console app:etl:inspect-datapool importEngine articles --with-count --show-create-sql
```
### 6.6 Native Staging Runtime
- `app:etl:list-native-staging-datapools [--tenant=default]`
zeigt tenant-isolierte Staging-Datapools der nativen Managed-Import- und Transform-Runtime
- `app:etl:inspect-native-staging-datapool <name> [--tenant=default] [--limit=5]`
zeigt Metadaten und Beispielzeilen eines nativen Staging-Datapools
- `app:etl:export-native-staging-csv <name> <target-file> [--tenant=default] [--delimiter=;]`
schreibt einen nativen Staging-Datapool als lokale CSV-Datei im Container
Beispiele:
```bash
php /srv/app/symfony/bin/console app:etl:plan import codex_native_import_smoke_engine --tenant=default
php /srv/app/symfony/bin/console app:etl:run-engine import codex_native_import_smoke_engine --tenant=default
php /srv/app/symfony/bin/console app:etl:list-native-staging-datapools --tenant=default
php /srv/app/symfony/bin/console app:etl:inspect-native-staging-datapool codex_native_import_smoke --tenant=default
php /srv/app/symfony/bin/console app:etl:export-native-staging-csv commerce_feed_google_merchant /srv/app/var/export/google-merchant.csv --tenant=default
```
Weitere Beispiele fuer den nativen Import-plus-Unitize-Pfad:
```bash
php /srv/app/symfony/bin/console app:etl:plan unitize codex_native_unitize_engine --tenant=default
php /srv/app/symfony/bin/console app:etl:run-engine import codex_native_unitize_import_engine --tenant=default
php /srv/app/symfony/bin/console app:etl:run-engine unitize codex_native_unitize_engine --tenant=default
php /srv/app/symfony/bin/console app:etl:run-pipeline codex_native_unitize_pipeline --tenant=default
php /srv/app/symfony/bin/console app:etl:inspect-native-staging-datapool codex_native_unitize_target --tenant=default
```
Verifizierte erweiterte Beispiele fuer den nativen Unitize-Query-Pfad:
```bash
php /srv/app/symfony/bin/console app:etl:run-engine import codex_native_join_left_import --tenant=default
php /srv/app/symfony/bin/console app:etl:run-engine import codex_native_join_right_import --tenant=default
php /srv/app/symfony/bin/console app:etl:run-engine unitize codex_native_join_unitize --tenant=default
php /srv/app/symfony/bin/console app:etl:inspect-native-staging-datapool codex_join_target --tenant=default
php /srv/app/symfony/bin/console app:etl:run-engine import codex_native_sql_a_import --tenant=default
php /srv/app/symfony/bin/console app:etl:run-engine import codex_native_sql_b_import --tenant=default
php /srv/app/symfony/bin/console app:etl:run-engine unitize codex_native_sql_unitize --tenant=default
php /srv/app/symfony/bin/console app:etl:inspect-native-staging-datapool codex_sql_target --tenant=default
```
Dabei wurde am 12. Maerz 2026 verifiziert:
- `codex_native_join_unitize` schreibt im Query-Mode `generated` genau eine Zielzeile aus `Left_Join` plus `Read_Conditions`
- `codex_native_sql_unitize` schreibt im Query-Mode `sql` aggregierte Ergebniszeilen mit `GROUP_CONCAT` und `UNION`
- alle Smoke-Test-Definitionen, Datapools und Runs wurden danach wieder entfernt
Verifizierter nativer Import-plus-Enhance-plus-Export-Pfad am 14. Maerz 2026:
```bash
php /srv/app/symfony/bin/console app:etl:plan enhance codex_native_transform_enhance --tenant=default
php /srv/app/symfony/bin/console app:etl:plan export codex_native_transform_export --tenant=default
php /srv/app/symfony/bin/console app:etl:plan pipeline codex_native_transform_pipeline --tenant=default
php /srv/app/symfony/bin/console app:etl:run-pipeline codex_native_transform_pipeline --tenant=default
php /srv/app/symfony/bin/console app:etl:inspect-native-staging-datapool codex_native_transform_enhanced --tenant=default
php /srv/app/symfony/bin/console app:etl:inspect-native-staging-datapool codex_native_transform_exported --tenant=default
```
- `codex_native_transform_pipeline` lief voll nativ als `native_staging_pipeline`
- `codex_native_transform_enhance` lief nativ als `native_enhance_to_staging` und filterte eine von zwei Zeilen ueber einen stringbasierten `Filter`
- `codex_native_transform_export` lief nativ als `native_export_to_staging`
- verifiziert wurden dabei unter anderem `trim`, `tolower`, `str_pad`, `replace_empty`, `concat_source` und `add_prefix`
- die Zielzeilen wurden in PostgreSQL-Staging geschrieben und danach vollstaendig wieder entfernt
Verifizierter nativer Pool-Filter-Pfad am 15. Maerz 2026:
```bash
php /srv/app/symfony/bin/console app:etl:plan export codex_poolfilter_export --tenant=default
php /srv/app/symfony/bin/console app:etl:plan pipeline codex_poolfilter_pipeline --tenant=default
php /srv/app/symfony/bin/console app:etl:run-pipeline codex_poolfilter_pipeline --tenant=default
php /srv/app/symfony/bin/console app:etl:inspect-native-staging-datapool codex_poolfilter_result --tenant=default --limit=10
```
Dabei wurde lokal gegen die Tool-DB verifiziert:
- `codex_poolfilter_export` plant als `native_export_to_staging`
- `codex_poolfilter_pipeline` plant als `native_staging_pipeline`
- die Pipeline schrieb genau eine Zielzeile in `codex_poolfilter_result`
- drei von vier Quellzeilen wurden nativ herausgefiltert
- die gespeicherten Datapool-Metadaten enthielten `filtered_row_count = 3`
- `filter_reasons` enthielt `draft row`, `blocked title marker`, `sku denied` und `missing allow mapping`
- alle Testdefinitionen, Datapools und Runs wurden danach wieder entfernt
Verifizierter nativer Cross-Dataset-Filter-Pfad am 15. Maerz 2026:
```bash
php /srv/app/symfony/bin/console app:etl:plan export codex_cross_dataset_export_a --tenant=default
php /srv/app/symfony/bin/console app:etl:plan export codex_cross_dataset_export_b --tenant=default
php /srv/app/symfony/bin/console app:etl:plan export codex_cross_dataset_export_c --tenant=default
php /srv/app/symfony/bin/console app:etl:plan pipeline codex_cross_dataset_pipeline --tenant=default
php /srv/app/symfony/bin/console app:etl:run-pipeline codex_cross_dataset_pipeline --tenant=default
php /srv/app/symfony/bin/console app:etl:inspect-native-staging-datapool codex_cross_dataset_result_a --tenant=default --limit=10
php /srv/app/symfony/bin/console app:etl:inspect-native-staging-datapool codex_cross_dataset_result_b --tenant=default --limit=10
php /srv/app/symfony/bin/console app:etl:inspect-native-staging-datapool codex_cross_dataset_result_c --tenant=default --limit=10
```
Dabei wurde lokal gegen die Tool-DB verifiziert:
- `codex_cross_dataset_export_a`, `codex_cross_dataset_export_b` und `codex_cross_dataset_export_c` planten jeweils als `native_export_to_staging`
- `codex_cross_dataset_pipeline` plante als `native_staging_pipeline` und lief erfolgreich voll nativ
- `codex_cross_dataset_result_a` enthielt genau die erwartete Keep-Zeile; `filter_reasons` zaehlte `duplicate ordernumber`, `no picture family` und `slow delivery`
- `codex_cross_dataset_result_b` enthielt genau die erwarteten Zeilen fuer vorhandenen Parent und Plain-Fall; `filter_reasons` zaehlte `marketplace orders` und `variants without parents`
- `codex_cross_dataset_result_c` enthielt genau die erwarteten Variant-Zeilen; `filter_reasons` zaehlte `non parents only`
- alle Testdefinitionen, Datapools und Runs wurden danach wieder vollstaendig entfernt
Verifizierte weitere SQL-Funktionsabdeckung am 13. Maerz 2026:
```bash
php /srv/app/symfony/bin/console app:etl:run-engine import codex_native_trim_idealo_import --tenant=default
php /srv/app/symfony/bin/console app:etl:run-engine import codex_native_trim_google_import --tenant=default
php /srv/app/symfony/bin/console app:etl:plan unitize codex_native_trim_unitize --tenant=default
php /srv/app/symfony/bin/console app:etl:run-engine unitize codex_native_trim_unitize --tenant=default
php /srv/app/symfony/bin/console app:etl:inspect-native-staging-datapool codex_trim_target --tenant=default
```
- `codex_native_trim_unitize` lief nativ im Query-Mode `sql`
- verschachtelte MySQL-Trim-Syntax mit `TRIM(LEADING ...)`, `TRIM(TRAILING ...)` und `TRIM(BOTH ... FROM ...)` wurde korrekt in SQLite-kompatible Funktionen uebersetzt
- die Ergebniszeilen wurden tenant-isoliert in PostgreSQL-Staging geschrieben und danach wieder vollstaendig entfernt
## 7. Deployment und Betrieb
### 7.1 Compose-Stack starten oder aktualisieren
```bash
ssh ortho-live 'cd /opt/containers/drive-dataengine && docker compose -f compose.yaml build dashboard && docker compose -f compose.yaml up -d dashboard'
```
### 7.2 Stack-Status pruefen
```bash
ssh ortho-live 'cd /opt/containers/drive-dataengine && docker compose -f compose.yaml ps'
```
### 7.3 Oeffentliche Erreichbarkeit pruefen
```bash
curl -sS -I https://dataengine.domgan.de
```
### 7.4 Datenbankschema anwenden
Wenn sich `docker/postgres/init/001-rls.sql` geaendert hat und der Container bereits initialisiert wurde:
```bash
ssh ortho-live 'cd /opt/containers/drive-dataengine && docker compose -f compose.yaml exec -T postgres psql -U dataengine -d dataengine -f /docker-entrypoint-initdb.d/001-rls.sql'
```
### 7.5 Repo-Guardrail lokal pruefen
Vor Pushes oder Releases kann die Doku-Synchronitaet lokal geprueft werden:
```bash
cd /opt/containers/drive-dataengine
./scripts/check-doc-sync.sh HEAD^ HEAD
```
## 8. Dokumentationsstandard
Diese Anleitung ist die operative Fuehrungsdokumentation fuer den aktuellen Stand.
Pflicht bei jeder Aenderung:
1. `README.md` aktualisieren, wenn Einstieg oder Architektur betroffen sind.
2. Diese Bedienungsanleitung aktualisieren, wenn Bedienung, Commands, Deployment oder Grenzen betroffen sind.
3. `docs/replatforming-plan.md` aktualisieren, wenn sich Zielbild oder Migrationsgrenze verschiebt.
Details stehen in `CONTRIBUTING.md`.
## 9. Entwicklungsprozess
Weiterentwicklung folgt ab jetzt diesem Ablauf:
1. Tool-Ebene und Anwendungsebene getrennt modellieren.
2. Neue Tool-Funktionalitaet zuerst gegen lokale Container und die lokale PostgreSQL des Systems testen.
3. Keine Tests gegen Fremdsysteme oder externe Server fahren, solange sie nicht ausdruecklich freigegeben sind.
4. Neue GUI-Funktionen immer ueber dieselben Services bedienen wie CLI und Worker.
5. Logs, Container-Status und lokale DB nutzen, um Fehler iterativ zu beseitigen.
6. Neue Runtime-Pfade mit lokalen Smoke-Tests gegen die Tool-DB verifizieren und Testartefakte danach wieder entfernen.
7. Bei schreibenden Runtime-Pfaden auch Wiederholungs- und Parallel-Trigger gegen die lokale Tool-DB pruefen.
8. Erst nach erfolgreicher lokaler Verifikation auf `ortho-live` deployen.
## 10. GitHub und Versionierung
Der GitHub-Stand wird ohne produktive Secrets versioniert.
Nicht versioniert werden:
- `.env`
- SSH-Schluessel
- Cloud-Service-Account-Dateien
- Legacy-Connection-Profile mit Live-Credentials
Fuer diese Dateien bleibt der Server die Quelle. Neue Umgebungen muessen die Secrets separat erhalten.
Das Repository erzwingt auf GitHub ueber `scripts/check-doc-sync.sh` und `.github/workflows/repository-guardrails.yml`, dass Code- und BetriebsAenderungen nicht ohne aktualisierte Dokumentation gemerged werden.
Privates Repository:
- `https://github.com/dominikgantenberg/drive-dataengine-clean`
Das fruehere private Repository `drive-dataengine` wurde geloescht und darf nicht wieder angelegt oder verwendet werden.
### 10.1 Legacy-Secrets
Historische Hilfs- und Testskripte im Legacy-Bestand laden produktive Zugangsdaten nicht mehr aus getrackten PHP-Dateien, sondern aus:
- `backend/secrets/legacy_runtime_secrets.php`
Diese Datei bleibt serverlokal und wird nicht versioniert.
Die Struktur fuer neue Umgebungen steht in:
- `backend/secrets/legacy_runtime_secrets.example.php.dist`
## 11. Bekannte Grenzen
- Managed File-Importe und eine wachsende native Transform-Teilmenge fuer `unitize`, `enhance` und `export` mit `direct`, `generated` und `sql` laufen derzeit voll nativ; dazu gehoeren jetzt auch MySQL-Trim-Modifikatoren, stringbasierte Filters, `Equals`, `In_String`, `In_Pool_Filter_Adapter`, `Not_In_Pool_Filter_Adapter` und eine breite Auswahl haeufiger Field-Adapter.
- ODBC-Profile sind noch nicht nativ in Symfony nutzbar.
- Read-only Datenbank-Probes haengen von der Netzwerkerreichbarkeit des Zielsystems vom Host `ortho-live` ab.
- Native Datapool-Write- und Import-Operationen decken aktuell nur MySQL ab; andere Treiber bleiben im Legacy-Pfad.
- Native Transform-Runtime deckt noch nicht die gesamte Legacy-Semantik ab; insbesondere Cross-Field-, File-Field- und Cross-Dataset-Filter-Adapter bleiben vorerst im Legacy-Pfad.
- Managed Definitions werden tenant-spezifisch in PostgreSQL gepflegt, in Planung und Katalog priorisiert und fuer nicht-native Runs als isolierter Overlay-Snapshot materialisiert.
- Der lokale Mount des Projektpfads kann instabil sein; im Stoerungsfall wird ueber `ssh/scp` gearbeitet.