This repository has been archived on 2026-09-17. You can view files and clone it, but you cannot make any changes to its state, such as pushing and creating new issues, pull requests or comments.
nixos-config/docs/hdm-klon-plan.md
pijukka 5407a1d235 Atomare Self-Close-Sessions, Eskalationskette und Reviewer-Schwelle für Herdr-Rollen
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
2026-09-01 19:33:39 +02:00

14 KiB
Raw Permalink Blame History

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.spaces 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, nichtupdate_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).