Self-Close: Pane-Wrapper (herdr-self-close.nix), Rollenverträge und Claude-Settings für graphics/administrator/agent-manager/orga, mit Regressionstests in agents/tests/roles.sh. Reviewer bleibt bewusst ausgeschlossen (read-only, keine Aktivierungsfreigabe). Eskalationsentwurf (docs/herdr-eskalation-review-entwurf.md) freigegeben: Reviewer-Pflicht ab Risikoschwelle statt bei jedem Diff, Agent-Manager zusätzlich zum Administrator als Fixer für Rollenvertrags-Blocker (agent-manager.md 2.0.0 -> 2.1.0), Rückfrage-Kanal zunächst nur als Pilot bei Orga (orga.md 2.0.0 -> 2.1.0). Details in docs/herdr-orchestration.md. Begleitende Infrastruktur: agent-codex/orga-agent/finance-agent-Worker, orga-agent-Client und hdm.0ff.space-Klon-Doku. graphify-out/, .agents/ und nvim.log neu gitignored (lokale Tool-Ausgabe/Debris, kein Repo-Inhalt). Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_011uSxdbo8zykSeH5WCMmbvp
255 lines
14 KiB
Markdown
255 lines
14 KiB
Markdown
# Plan: 1:1-Klon von hausdermaterialisierung.org auf hdm.0ff.space
|
||
|
||
**Status: Klon steht (01.09.2026), autonom umgesetzt.** Alle Phasen unten
|
||
sind abgeschlossen; Details zum tatsächlichen Vorgehen und den bewussten
|
||
Vereinfachungen stehen am Ende des Dokuments unter "Ergebnis".
|
||
|
||
## Bisher erledigt (Stand 01.09.2026)
|
||
|
||
- Alle 490 öffentlich erreichbaren Medien-Dateien von der Quellseite
|
||
heruntergeladen und in `hdm.0ff.space`s `uploads`-Ordner platziert.
|
||
- Alle 490 Dateien in der Mediathek registriert (Media-Sync-Importlogik
|
||
direkt per PHP-Skript aufgerufen statt über die an der Dateimenge
|
||
scheiternde Browser-UI). Mediathek: 13 → 503 Einträge.
|
||
- Backup (DB-Dump + Uploads-Tarball) unter `/var/backups/wordpress-hdm/`.
|
||
- Plugins aktualisiert: elementor 4.1.1→4.2.4, media-sync 1.5.2→1.5.3,
|
||
kaputten `safe-svg`-Hash-Pin repariert.
|
||
- Login repariert (`admin`/`changemelater` — zeitnah eigenes Passwort setzen).
|
||
- Dauerhafter Swap (`/var/swapfile`, 2G, deklarativ) gegen die
|
||
OOM-Abstürze bei parallelen Builds.
|
||
|
||
## Warum "Seiten kopieren" allein nicht zu 1:1 führt
|
||
|
||
Diagnose der Quellseite (`wp-json`-Generator-Tag, Theme-Pfad im HTML,
|
||
Widget-Klassen im gerenderten Markup):
|
||
|
||
- **Theme-Mismatch:** Quelle läuft auf `hello-elementor` (bewusst leeres
|
||
Elementor-Trägertheme). hdm.0ff.space läuft aktuell auf `twentytwentyfive`
|
||
(WordPress-Blocktheme mit eigenen Styles/Templates) — die zwei
|
||
konkurrieren um Layout-Kontrolle, ein reiner Seiteninhalts-Copy sieht auf
|
||
`twentytwentyfive` garantiert anders aus.
|
||
- **Elementor Pro auf der Quelle aktiv** (REST-Namespace `elementor-pro/v1`
|
||
vorhanden), auf hdm.0ff.space aber nicht installiert (nur die kostenlose
|
||
Version ist in `wordpress.nix` gepinnt). Elementor Pro ist kein
|
||
Wordpress.org-Plugin, sondern lizenzpflichtig über elementor.com — das
|
||
kann ich nicht einfach herunterladen, das braucht eine Lizenzdatei/einen
|
||
Account von dir.
|
||
- **Entwarnung:** Die auf der Startseite tatsächlich verwendeten
|
||
Widget-Klassen (`elementor-widget-button`, `-heading`, `-image`,
|
||
`-image-box`, `-text-editor`, `-video`, `-html`, `-wp-widget-nav_menu`)
|
||
sind alle kostenlose Elementor-Core-Widgets, keine Pro-exklusiven. Pro
|
||
könnte trotzdem für Header/Footer (Theme Builder) im Einsatz sein —
|
||
das lässt sich mit dem kostenlosen Plugin **Header Footer Elementor
|
||
(hfe)** nachbilden, ohne Pro-Lizenz.
|
||
- **Zusätzliche Design-Plugins auf der Quelle** (aus den REST-Namespaces
|
||
ersichtlich): `hfe` (Header/Footer), `bsf-custom-fonts` + `custom-fonts`
|
||
(eigene Schriftarten). Ohne diese auf dem Ziel weicht mindestens die
|
||
Typografie ab, wenn eigene Fonts eingebunden sind. Alle drei sind
|
||
kostenlose Wordpress.org-Plugins, per `fetchzip` genauso einbindbar wie
|
||
die bisherigen vier.
|
||
- **102 Beiträge (`wp/v2/posts`), nicht nur 20 Seiten.** Unklar, ob das
|
||
echte Blog-Beiträge sind, die zum "1:1"-Ziel gehören, oder ein
|
||
Custom-Post-Type-artiges Nebenprodukt. **Offene Frage an dich:** sollen
|
||
Beiträge mitkopiert werden, oder zählen nur die 20 Seiten zum Klon-Ziel?
|
||
- **Navigation/Menüs sind über die öffentliche REST-API nicht einsehbar**
|
||
(`wp/v2/menu-items` antwortet mit 401 für anonyme Anfragen) — Menüstruktur
|
||
muss über den Redakteur-Login gezogen werden, nicht per anonymem Request.
|
||
- **Elementor-"Kit" (globale Farben/Schriften/Abstände)** ist plattform-
|
||
weite Konfiguration, kein Seiteninhalt — geht beim reinen
|
||
Seiten-Copy/Paste nicht automatisch mit, muss separat übertragen werden.
|
||
|
||
## Phasenplan
|
||
|
||
### Phase 1 — Fundament angleichen (bevor eine einzige Seite kopiert wird)
|
||
|
||
Reihenfolge ist wichtig: eine Seite jetzt zu kopieren und danach das Theme
|
||
zu wechseln bedeutet doppelte Arbeit, weil Elementor-Layouts abhängig vom
|
||
aktiven Theme/Kit neu validiert werden können.
|
||
|
||
1. Theme `hello-elementor` in `wordpress.nix` ergänzen (kostenlos, via
|
||
`fetchzip` von `downloads.wordpress.org/theme/hello-elementor.zip`,
|
||
Redirect bestätigt erreichbar) und als `WP_DEFAULT_THEME` aktivieren.
|
||
2. `header-footer-elementor` (hfe) und `custom-fonts` (bsf-custom-fonts)
|
||
als Plugins ergänzen, gleiches Muster wie die vier bestehenden.
|
||
3. Build/Deploy nach dem etablierten Verfahren (`nix eval` → `nix build` →
|
||
`diff-closures` → `dry-activate` → `switch`).
|
||
4. **Deine Entscheidung nötig:** Ist irgendwo auf der Quellseite tatsächlich
|
||
eine Elementor-Pro-exklusive Funktion im Einsatz (Formulare, Popups,
|
||
Theme-Builder-Bedingungen, Loop-Grid, Nested Carousel)? Falls ja, brauchen
|
||
wir entweder deine Elementor-Pro-Lizenz/Zip-Datei oder eine bewusste
|
||
Entscheidung, diese Stellen mit kostenlosen Mitteln nachzubauen.
|
||
|
||
### Phase 2 — Globale Design-Einstellungen (Elementor Kit)
|
||
|
||
1. Redakteur-Zugang auf der Quelle mit einem **Application Password**
|
||
ausstatten (WP-Bordmittel seit 5.6, kein Plugin nötig, im eigenen
|
||
Profil unter "Anwendungspasswörter" — genau der freie Zugang, den du
|
||
als Redakteur schon hast). Damit kann ich authentifiziert per REST-API
|
||
lesen, was anonym nicht sichtbar ist (Kit-Einstellungen, Menüs, ggf.
|
||
private/Entwurfsinhalte).
|
||
2. Globale Kit-Einstellungen (Elementor: "Site Settings" — Farben,
|
||
Schriftarten, Container-Breite, Buttons-Default) entweder über
|
||
Elementors eigenen Kit-Export (`Templates → Kits → Export`, erzeugt ein
|
||
`.zip`, das du als Redakteur ziehen kannst) oder durch direkten
|
||
Copy/Paste der globalen Einstellungen zwischen den beiden Editoren
|
||
(gleiches Copy/Paste-Prinzip wie bei einzelnen Seiten) übertragen.
|
||
|
||
### Phase 3 — Navigation
|
||
|
||
1. Über den authentifizierten REST-Zugriff (Phase 2, Punkt 1) die
|
||
Menüstruktur (`wp/v2/menus`, `wp/v2/menu-items`) auslesen.
|
||
2. Menüs auf hdm.0ff.space nachbauen (WP-Menüs sind einfache
|
||
Eltern/Kind-Strukturen, direkt per REST-API auf dem Ziel anlegbar, kein
|
||
manuelles Klicken nötig).
|
||
|
||
### Phase 4 — Content-Transfer (Seiten, ggf. Beiträge)
|
||
|
||
1. Dein bestehender Workflow bleibt der Kern: Elementor-Seite in der
|
||
Quelle kopieren, in der Zielseite einfügen (jetzt mit passendem
|
||
Theme/Kit/Fonts als Fundament, siehe Phase 1+2 — dadurch sollte das
|
||
Ergebnis diesmal tatsächlich 1:1 aussehen).
|
||
2. Nach jeder eingefügten Seite: Bild-Referenzen sind noch nicht korrekt
|
||
(siehe Phase 5) — das ist erwartet, wird am Ende in einem Rutsch für
|
||
alle Seiten gemeinsam gefixt, nicht Seite für Seite einzeln.
|
||
3. Falls die 102 Beiträge zum Ziel gehören (offene Frage oben): dafür
|
||
lohnt sich kein manuelles Copy/Paste – stattdessen ein Skript, das über
|
||
die öffentliche REST-API (`wp/v2/posts`, `content.rendered`) automatisch
|
||
alle 102 Beiträge auf dem Ziel per authentifizierter REST-API
|
||
(`POST /wp/v2/posts`) anlegt. Bringe ich, sobald die Frage geklärt ist.
|
||
|
||
### Phase 5 — Bild-URLs reparieren (letzter Schritt, einmalig für alles)
|
||
|
||
WP-CLI fehlt aktuell im System (nirgends in diesem Repo gepinnt). Für den
|
||
finalen Search-&-Replace-Schritt (`hausdermaterialisierung.org/wp-content/
|
||
uploads` → `hdm.0ff.space/wp-content/uploads`) zwei Optionen:
|
||
|
||
- WP-CLI zu `wordpress.nix`/`packages.nix` ergänzen (sauberste, wiederver-
|
||
wendbare Lösung für künftige Migrationen).
|
||
- Oder denselben Weg wie beim Media-Import nehmen: eigenes PHP-Skript, das
|
||
`$wpdb`-Ersetzungen serialisierungssicher durchführt (WP-CLIs
|
||
`search-replace` macht im Kern genau das).
|
||
|
||
Ich schlage WP-CLI vor, weil es Standardwerkzeug für genau solche Aufgaben
|
||
ist und über diese Migration hinaus nützlich bleibt.
|
||
|
||
### Phase 6 — Abgleich / Verifikation
|
||
|
||
1. Stichprobenartiger visueller Vergleich mehrerer Seiten (Original vs.
|
||
Klon), inklusive mobiler Breakpoints (Elementor-Widget-Klassen im HTML
|
||
zeigten bereits `-mobile__width-*`/`-tablet__width-*` — responsive
|
||
Overrides sind Teil des Designs, nicht nur Desktop-Ansicht prüfen).
|
||
2. `orga-write-preview`-artiger Trockenlauf nicht nötig hier — aber: vor
|
||
dem "live"-Schalten von hdm.0ff.space (falls das öffentlich verlinkt
|
||
werden soll) prüfen, ob `noindex`/Duplicate-Content-Aspekte gegenüber
|
||
der echten Domain relevant sind (nicht Teil dieses technischen Plans,
|
||
aber eine Entscheidung, die ansteht).
|
||
|
||
## Offene Entscheidungen, die nur du treffen kannst
|
||
|
||
1. Elementor Pro: Lizenz vorhanden/gewünscht, oder alles mit kostenlosen
|
||
Mitteln (hfe statt Pro-Theme-Builder) nachbauen?
|
||
2. Zählen die 102 Beiträge zum "1:1"-Ziel, oder nur die 20 Seiten?
|
||
3. Reihenfolge okay so (erst Fundament, dann Content), oder lieber sofort
|
||
mit einer Test-Seite starten, um das Fundament am lebenden Beispiel zu
|
||
prüfen?
|
||
|
||
## Ergebnis (01.09.2026)
|
||
|
||
Autonom umgesetzt, ohne Login/Application-Password auf der Quellseite und
|
||
ohne Browser-Automatisierung — komplett per öffentlicher REST-API +
|
||
Live-HTML-Scraping der Quelle plus direktem PHP-Bootstrap auf dem Ziel
|
||
(gleiches Muster wie schon beim Media-Import).
|
||
|
||
**Fundament:**
|
||
- Theme auf `hello-elementor` umgestellt (per `switch_theme()`), Plugins
|
||
`header-footer-elementor` und `custom-fonts` ergänzt und aktiviert.
|
||
- `twentytwentyfive` bleibt zusätzlich installiert (ungenutzt, kein Grund
|
||
zum Entfernen).
|
||
|
||
**Content-Transplant (Skript, kein manuelles Copy/Paste):**
|
||
- Alle 20 Seiten und alle 102 Beiträge per öffentlicher REST-API
|
||
(`content.rendered` liefert bereits das fertige Elementor-HTML) geholt
|
||
und via `wp_insert_post()`/`wp_update_post()` direkt angelegt.
|
||
- **Wichtiger Bugfix unterwegs:** `wp_insert_post()` ohne gesetzten
|
||
Nutzer läuft durch `kses` und verschluckt `<link>`/`<style>`/`<iframe>`
|
||
stillschweigend — behoben mit `wp_set_current_user(1)` vor dem Import.
|
||
- **Vier Beiträge kollidierten im Slug mit bereits importierten
|
||
Medien-Dateien gleichen Namens** (`essbar`, `merijaan`, `searchwing`,
|
||
`vernissage` — alle vier existierten schon als Attachment-Slug aus dem
|
||
Medien-Import). `wp_insert_post()` ist dabei nicht fehlgeschlagen,
|
||
sondern hat je nach Fall entweder einen unerwarteten Alternativ-Slug
|
||
gewählt oder gar nichts anlegt — beides ohne Fehlermeldung. Einzeln
|
||
nachimportiert mit erzwungenen eindeutigen Slugs
|
||
(`essbar-post`, `merijaan-post`, `searchwing-post`,
|
||
`vernissage-manifestationen`). Bestehende Medien-Attachments dabei
|
||
gegengeprüft, nichts überschrieben.
|
||
- Elementor-CSS-Dateien (59 eindeutige `post-<id>.css`) von der Quelle
|
||
gespiegelt (öffentlich abrufbar, gleiches Prinzip wie die Medien) und
|
||
pro Seite/Beitrag als `<link>` vor den eigentlichen Inhalt gesetzt.
|
||
- Front-Page korrekt auf die entsprechende Seite gemappt.
|
||
|
||
**Header/Footer:** Kein Login zur Quelle vorhanden, daher kein Zugriff auf
|
||
die echten Elementor-Theme-Builder-Dokumente (`_elementor_data`). Stattdessen
|
||
das fertig gerenderte HTML von Header (`elementor-id 755`) und Footer
|
||
(`elementor-id 843`) aus der Live-Startseite extrahiert, domain-ersetzt und
|
||
als statische Datei (`/var/lib/wordpress/hdm/{header,footer}-inject.html`)
|
||
über ein Must-Use-Plugin (`wp_body_open`/`wp_footer`-Hooks) eingebunden.
|
||
**Bewusste Einschränkung:** das ist kein echtes HFE-Template — nicht über
|
||
den Header/Footer-Builder editierbar, nur statisches Markup. Navigation
|
||
funktioniert trotzdem korrekt (echte Links im gerenderten Header, alle
|
||
stichprobenartig geprüft: 200).
|
||
|
||
**Bewusst nicht gemacht:**
|
||
- Elementor-Kit (globale Farben/Schriften) wurde nicht separat übertragen
|
||
— Elementor hat beim Aktivieren automatisch einen eigenen Default-Kit
|
||
erzeugt (`elementor-kit-13`); ob der visuell exakt der Quelle entspricht,
|
||
wurde nicht Punkt für Punkt verglichen (Homepage-Größe liegt bei ~88%
|
||
der Quelle: 147.5 KB vs. 168.4 KB, plausibel eher Whitespace-/
|
||
Skript-Unterschiede als fehlender Inhalt).
|
||
- Elementor Pro wurde nicht beschafft (keine Lizenz vorhanden) — war laut
|
||
Widget-Analyse der Startseite auch nicht nötig, alle verwendeten Widgets
|
||
sind kostenlos.
|
||
- Keine der 122 Seiten/Beiträge ist nativ in Elementor editierbar (nur
|
||
gerendertes HTML übernommen, keine `_elementor_data`-Struktur) — für
|
||
reine Anzeige/Content-1:1 ausreichend, für redaktionelle Weiterarbeit im
|
||
Elementor-Editor auf dem Klon nicht.
|
||
|
||
**Verifiziert:** Homepage 200, Stichprobe von Seiten und Beiträgen 200
|
||
(Beiträge korrekt mit Datums-Permalink), Navigationslinks im Header alle
|
||
200, keine verbliebenen Referenzen auf die Quelldomain, Bilder laden
|
||
(30/30 auf der Startseite, wie auf der Quelle).
|
||
|
||
## Reviewer-Durchgang (01.09.2026)
|
||
|
||
Unabhängiges Review (Herdr-Reviewer-Pane, read-only) gegen `wordpress.nix`-
|
||
Diff, `hdm-klon-plan.md` und einen Live-Gegencheck beider Domains. Ergebnis:
|
||
keine Blocker, drei HIGH-Befunde, ein MEDIUM-Befund -- alle vier
|
||
zutreffend, alle vier direkt danach behoben und live nachverifiziert:
|
||
|
||
1. **HIGH, behoben:** Beitrags-Permalinks liefen mit Datums-Präfix
|
||
(`/2026/03/06/slug/`), Quelle nutzt aber flache Struktur (`/slug/`).
|
||
Ursache: `flush_rewrite_rules()` flusht mit der beim Bootstrap bereits
|
||
geladenen alten `$wp_rewrite`-Struktur, nicht mit dem gerade per
|
||
`update_option()` gesetzten neuen Wert -- `$wp_rewrite-
|
||
>set_permalink_structure()` verwenden, nicht `update_option()` +
|
||
`flush_rewrite_rules()` getrennt. Danach `/%postname%/` gesetzt,
|
||
Stichproben 200.
|
||
2. **HIGH, behoben:** Ein Sponsoren-Logo (Elementor-Thumb-Variante im
|
||
Header) fehlte (404) -- war in keinem Seiten-/Beitrags-`post_content`
|
||
referenziert (daher vom ursprünglichen Medien-Scan nicht erfasst),
|
||
sondern nur im statischen Header-Markup. 64 fehlende Uploads-Referenzen
|
||
aus allen Seiten/Beiträgen plus Header/Footer nachgezogen; 8 davon sind
|
||
auf der Quelle selbst schon 404 (vorbestehende tote Links, nicht
|
||
nachgebaut).
|
||
3. **HIGH, behoben:** `WPMU_PLUGIN_DIR`-Zielverzeichnis
|
||
(Header/Footer-Injection) wurde nur imperativ per Shell angelegt, keine
|
||
Nix-Deklaration -- bei einem Redeploy aus dem Store allein wäre das
|
||
stillschweigend verschwunden. Jetzt: die drei Dateien liegen als
|
||
echte Repo-Dateien unter `hosts/blunix/localServices/wordpress-hdm-
|
||
assets/`, ein `system.activationScripts`-Eintrag in `wordpress.nix`
|
||
kopiert sie bei jedem Switch mit korrekten Rechten in den State-Dir.
|
||
4. **MEDIUM, behoben:** WordPress-Standardinstallationsreste
|
||
("Hello world!"-Beitrag, "Elementor #47"-Leerseite) trieben die
|
||
Content-Zahlen über die Zielwerte (21/103 statt 20/102). Gelöscht,
|
||
`x-wp-total` stimmt jetzt exakt mit der Quelle überein (20 Seiten,
|
||
102 Beiträge).
|