From 6a1dda0fc9b5c9c73ab97b75ec5dad977a814d04 Mon Sep 17 00:00:00 2001 From: Lucas Orth Date: Sun, 20 Sep 2026 12:50:12 +0200 Subject: [PATCH] Testlauf dokumentiert, README vervollstaendigt --- docs/testlauf.md | 217 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 217 insertions(+) create mode 100644 docs/testlauf.md diff --git a/docs/testlauf.md b/docs/testlauf.md new file mode 100644 index 0000000..3f57a9b --- /dev/null +++ b/docs/testlauf.md @@ -0,0 +1,217 @@ +# Testlauf-Protokoll + +Durchgefuehrt am 20.09.2026 gegen die echten Systeme, nicht simuliert. +Alle Executions liegen in n8n und sind dort nachvollziehbar. + +--- + +## Vorbereitung + +`artikel-index.json` wurde so gesetzt, dass nur ein Artikel auf `offen` stand, +um NeuronWriter-Kontingent zu sparen: + +``` +brief_bereit handgeschriebene-werbebriefe +pausiert handschriftliche-mailings-kundenbindung +pausiert response-rate-direktmarketing +offen dummy-testlauf +``` + +--- + +## Lauf 1: Workflow 1, Execution 167 + +Start per Webhook um 10:41:00, beendet 10:43:07, Status `success`. +24 Nodes gelaufen, kein einziger Fehlerpfad. + +Der Poll-Loop arbeitete wie ausgelegt: + +| Node | Durchlaeufe | +|---|---| +| `Warten` | 6 | +| `Analyse abfragen` | 6 | +| `Versuch zaehlen` | 5 | +| `Poll-Limit erreicht?` | 5 | + +Die Analyse war nach dem sechsten Poll fertig, also nach rund 120 Sekunden. +Von 30 erlaubten Versuchen wurden 6 gebraucht. + +**Ergebnis:** + +``` +Briefe vorbereitet: 1, Fehler: 0 + +Fertig: + dummy-testlauf | Zielwortzahl 839 | 12 Pflichtbegriffe, 29 Fragen + Datei: briefe/brief-dummy-testlauf.json + Analyse: https://app.neuronwriter.com/analysis/view/ff324721305a0a60 +``` + +**Gegenprobe im Repo:** + +- `briefe/brief-dummy-testlauf.json` angelegt, 19437 Bytes +- Commit `150017a9` "Brief erzeugt: dummy-testlauf (handgeschriebene briefe roboter)" +- Commit `4c4fc0f2` "Status auf brief_bereit gesetzt (1 Artikel)" +- Zeile in `artikel-index.json` steht auf `brief_bereit`, `query_id` nachgetragen + +Der Brief enthaelt alle geforderten Felder sowie den Zusatzblock: + +``` +query_id ff324721305a0a60 | keyword handgeschriebene briefe roboter | wortzahl_ziel 839 +begriffe_pflicht 12 | begriffe_erweitert 65 | fragen 29 +top-3 Fragen (nach Gewicht): + 10 Wo kann ich handschriftliche Briefe schreiben lassen? + 9 Wie kann ich Handschriften in digitalen Text umwandeln? + 9 Wie kann ich schnell handschriftlich schreiben? +``` + +--- + +## Lauf 2a: Workflow 2, Nachbesserungspfad, Execution 168 + +Zielwert 72, Testartikel hatte Score 44. 24 Nodes, alle `success`. + +``` +Importiert: 0 | Nachbessern: 1 | Fehler: 0 + +Score zu niedrig, Nachbesserung liegt im Repo: + handgeschriebene-werbebriefe: Score 44 von 72 + 9 Pflichtbegriffe fehlen, 71 Zusatzbegriffe + Wortzahl 96 von 1178 (-1082) + Datei: nachbessern/nachbessern-handgeschriebene-werbebriefe.json + +Kein Beitrag wurde veroeffentlicht. Alle Importe stehen auf Entwurf. +``` + +Der WordPress-Zweig wurde korrekt nicht betreten. + +--- + +## Lauf 2b: Workflow 2, Importpfad, Execution 169 + +Zielwert per Webhook auf 40 abgesenkt (`{"score_ziel":40}`), damit derselbe +Artikel den Importpfad nimmt. 32 Nodes, alle `success`. + +``` +Importiert: 1 | Nachbessern: 0 | Fehler: 0 + +Als Entwurf in WordPress: + handgeschriebene-werbebriefe (Score 44, neu angelegt) + Vorschau: https://skrift.de/?p=4210&preview=true + Bearbeiten: https://skrift.de/wp-admin/post.php?post=4210&action=edit + PREISE: 2 ausgeschriebene(r) Preis(e) im Text: 2,56 EUR, 0,95 €. + Bitte durch [skrift_preis key="..."] ersetzen. + Hinweis: Cache-Endpunkt antwortete mit HTTP 403. Kein Abbruchgrund. +``` + +**Gegenprobe in WordPress** (`GET /wp-json/wp/v2/posts/4210?context=edit`): + +``` +id 4210 | status draft | slug handgeschriebene-werbebriefe +kategorien [17] +kurzantwort gesetzt +cta_ziel https://skrift.de/muster-anfordern +stand_datum 2026-09-20 +_seopress_titles_title Handgeschriebene Werbebriefe: wann sie wirken +_seopress_titles_desc gesetzt +_seopress_analysis_target_kw handgeschriebene werbebriefe +faq 2 Eintraege +``` + +Die Kategorie `Direktmarketing` existierte nicht und wurde als ID 17 angelegt. + +--- + +## Lauf 2c: Idempotenz, Execution 170 + +Derselbe Artikel ein zweites Mal, gleiche Parameter. + +``` +post_id 4210 | aktion: aktualisiert | alter_status: draft | status: draft +Kategorie erneut angelegt? nein (bestehende wiederverwendet) +``` + +`GET /wp-json/wp/v2/posts?slug=handgeschriebene-werbebriefe&status=any` +liefert genau **einen** Treffer. Es entstand kein Duplikat. + +`status-importe.json` enthaelt eine Zeile, die beim zweiten Lauf ueberschrieben +statt angehaengt wurde: + +```json +[{"slug":"handgeschriebene-werbebriefe","score":44, + "datum":"2026-09-20T10:47:54.336Z","post_id":4210, + "vorschau_url":"https://skrift.de/?p=4210&preview=true", + "aktion":"aktualisiert","kategorie":"Direktmarketing"}] +``` + +--- + +## Zwei Fehler, die der Testlauf gefunden hat + +### 1. Webhook mit responseMode "lastNode" + +Der erste Versuch endete in einem 504 von openresty, waehrend der Workflow im +Hintergrund weiterlief. Ursache: Der Webhook hielt die HTTP-Verbindung bis zum +Workflow-Ende offen, der Reverse Proxy brach nach 60 Sekunden ab. +Beide Workflows nutzen jetzt `responseMode: "onReceived"`. + +### 2. body statt data bei fullResponse + +n8n 1.120 liefert den Antwortkoerper bei `fullResponse: true` im Feld `data`, +nicht `body`. Alle Existenzpruefungen griffen auf `.body` zu und haetten immer +"nicht vorhanden" ergeben. Folge waere gewesen: Workflow 2 legt bei jedem Lauf +einen neuen Beitrag an, statt den bestehenden zu aktualisieren. +An sieben Stellen korrigiert und versionsfest gemacht: + +```js +const K = (r) => (r && r.body !== undefined) ? r.body : ((r || {}).data); +``` + +Lauf 2c belegt, dass die Idempotenz jetzt haelt. + +--- + +## Netzwerkbefund: Gitea war aus n8n nicht erreichbar + +Der erste Lauf scheiterte mit `timeout of 60000ms exceeded` beim Lesen der +Artikeltabelle. Die Diagnose aus n8n heraus ergab: + +| Ziel | Ergebnis | +|---|---| +| `https://gitea.lucas-orth.de/api/v1/version` | `ECONNABORTED` | +| `http://gitea.lucas-orth.de/api/v1/version` | HTTP 301, Server openresty | +| `https://app.neuronwriter.com/...` | HTTP 401 (erreichbar) | +| `https://skrift.de/wp-json/` | HTTP 200 | +| `http://gitea:3000/api/v1/version` | **HTTP 200** | + +`gitea.lucas-orth.de` loest auf `100.98.99.19` auf, eine NetBird-Overlay-Adresse. +Der n8n-Host erreichte den dortigen openresty auf Port 80, aber nicht auf 443. + +Geloest ueber das Docker-Netz: beide Workflows sprechen Gitea jetzt als +`http://gitea:3000` an. Der Weg laeuft nicht ueber den Reverse Proxy und +nicht ueber das Overlay, ist damit schneller und faellt nicht aus, wenn am +Proxy etwas geaendert wird. + +Zum Nachtesten liegt der Hilfsworkflow `netzcheck` in n8n: + +```bash +curl -X POST https://n8n.lucas-orth.de/webhook/probe \ + -H "Content-Type: application/json" \ + -d '{"url":"http://gitea:3000/api/v1/version"}' +``` + +--- + +## Offene Punkte nach dem Testlauf + +**Cache leeren gibt HTTP 403.** Der Benutzer `ai-agent` hat als Redakteur nicht +die Berechtigung fuer `POST /wp-json/seopress/v1/commands/clear-cache`. +Der Workflow behandelt das als Hinweis, nicht als Fehler. Bei Entwuerfen ist ein +Cache-Purge ohnehin wirkungslos. Relevant wird es erst, wenn ein bereits +veroeffentlichter Beitrag aktualisiert wird. Wer das braucht, gibt der Rolle +Redakteur die noetige Capability oder leert nach dem Veroeffentlichen von Hand. + +**Testreste.** In WordPress steht Beitrag 4210 als Entwurf, dazu die Kategorie +`Direktmarketing` (ID 17). In NeuronWriter liegen zwei Analysen +(`565abcca58f65e52`, `ff324721305a0a60`) mit je einer importierten Revision. +Beides kann bleiben oder geloescht werden.