Workers P3: Betriebsanleitung nach der Endprüfung (Setup-Schlüssel, Absender-Prüfung, Such-Probe, Auslieferung mit Merge, Rückweg)
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
5b65bef4aa
commit
be2e6786b6
1 file changed
+312
-19
@@ -1153,23 +1153,316 @@ test('Nachrichten: Autor "mensch:<id>" → Name aus employees, "clara" → Clara
|
||||
|
||||
## Betriebsanleitung (Kenso)
|
||||
|
||||
Voraussetzung: P1 läuft (Container `webklar-workers`, nginx-Block `/api/workers/` steht) und P2 ist in WOMS live. Zugangsdaten tippst du nur selbst in `.env` ein, nie in einen Chat oder Prompt.
|
||||
Reihenfolge einhalten: Block A bis I. **[Claude]** macht Claude für dich, alles andere tippst du selbst auf dem Server. Zugangsdaten (Passwörter, Schlüssel) tippst du nur selbst ein (`read -rs` oder direkt in `.env`), nie in einen Chat und nie in einen Befehl, der sie ausgibt. Nach jeder Änderung an `.env` oder `docker-compose.yml` startest du mit `cd /home/webklar/apps && docker compose up -d --no-deps workers` neu (nicht `docker restart`: das liest `.env` nicht neu ein; nie ein nacktes `docker compose up -d`). Der Dienst sendet in P3 nichts an Interessenten: Antworten liegen als Entwurf im Reiter „Freigabe“, du schickst sie von Hand ab. **Achtung:** Nach dem Neustart in B5 suchen Rita und Finn jede Nacht ab 22:00 Uhr (Berlin) von selbst, und Bea bewertet die Funde (Rita und Finn kosten je Suchlauf etwa 0,10 bis 0,30 $, also bis etwa 0,60 $ je Nacht, dazu kommen die Bewertungen). Eine Antwort entsteht trotzdem erst, wenn du in Block G den Schalter setzt.
|
||||
|
||||
1. **Dienst-Stand übernehmen.** `git -C /home/webklar/apps/workers-dev log --oneline -25` (die P3-Commits liegen auf `p3-anfragen`), dann `git -C /home/webklar/apps/workers-dev push origin p3-anfragen:main` und `git -C /home/webklar/apps/workers pull --ff-only`. Prüfung: `git -C /home/webklar/apps/workers log --oneline -1` zeigt „P3: Verdrahtung, Datenschutz-Schalter und Pult-Probe“ oder neuer. Dieser `git pull` aktualisiert auch die Rollen-Personas `prompts/{rita,finn,bea,clara}.md` (H5): das Compose-Volume `./workers/prompts:/app/prompts:ro` aus P1 zeigt sie dem Dienst sofort, Textänderungen daran brauchen später **keinen** Neubau (der Dienst lädt eine Datei bei geänderter Änderungszeit neu).
|
||||
2. **Schema nachziehen (vor dem Neustart, sonst scheitert `updateSettings` am unbekannten Feld).**
|
||||
```
|
||||
cd /home/webklar/apps/workers-dev
|
||||
( export APPWRITE_API_KEY="$(sed -n 's/^WORKERS_APPWRITE_API_KEY=//p' /home/webklar/apps/.env | tail -n1)"; timeout 300 node --max-old-space-size=1024 scripts/setup-collections.mjs --check; echo "Exit $?" )
|
||||
```
|
||||
*Erwartet:* `Exit 1` mit genau zwei `FEHLT`-Zeilen (`workersSettings.privacyInteressentenLive`, Index `threadId` bei `workersMessages`). Derselbe Befehl ohne `--check`, danach wieder mit `--check` → `Exit 0`.
|
||||
3. **Haiku-Websuche prüfen.** Falls in P1 (Betriebsanleitung 8) noch nicht geschehen: `( ANTHROPIC_API_KEY="$(sed -n 's/^WORKERS_ANTHROPIC_API_KEY=//p' /home/webklar/apps/.env | tail -n1)" timeout 120 node --max-old-space-size=1024 scripts/check-modelle.mjs; echo "Exit $?" )` im Dev-Checkout. *Erwartet:* Haiku kann `web_search` (`Exit 0`); sonst druckt das Skript die Zeile `WORKERS_SEARCH_MODEL=claude-sonnet-5-5` für die `.env` (Rita und Finn suchen dann mit Sonnet).
|
||||
4. **Alarm-Postfach in `.env`.** `cp -p /home/webklar/apps/.env /home/webklar/apps/.env.bak-$(date +%F)-workers`, dann in `/home/webklar/apps/.env` ergänzen: `WORKERS_ALERT_IMAP_HOST=…`, `WORKERS_ALERT_IMAP_PORT=993`, `WORKERS_ALERT_IMAP_USER=…`, `WORKERS_ALERT_IMAP_PASS=…` (bei Google Workspace ein App-Passwort; alle vier oder keins). Der Compose-Block `workers` aus P1 reicht sie schon durch: `rg -c "WORKERS_ALERT_IMAP" /home/webklar/apps/docker-compose.yml` → `4`; `docker compose config -q` → Exit 0.
|
||||
5. **Image bauen und Dienst neu starten.** `cd /home/webklar/apps && free -m && docker compose build workers && docker compose up -d --no-deps workers`. *Prüfen:* `docker logs --tail 40 webklar-workers` (Zeile `workers listening on 8092`, keine Stacktraces) und `docker exec webklar-workers node -e "fetch('http://localhost:8092/api/workers/health').then(r=>r.json()).then(console.log)"` → `{ ok: true, … }`. Der erste Lauf des Alarm-Ticks setzt den Lesestand auf die aktuell höchste Mail (keine Altmails). Zusätzlich: `docker exec webklar-workers ls -l /app/prompts/clara.md /app/prompts/bea.md /app/prompts/rita.md /app/prompts/finn.md` zeigt die vier Dateien (Mount aus P1 ist da); fehlt eine, wirft der Dienst `Prompt fehlt: <id>` beim ersten Aufruf der Rolle.
|
||||
6. **Postfach prüfen (manuell).** `docker exec webklar-workers node scripts/check-alert-mailbox.mjs`. Erwartet: Zeilen mit `uidValidity`, `uidNext`, `exists` und Absender-Domains, kein Passwort. `IMAP_AUTH` bedeutet falsches Passwort: in `.env` korrigieren, Dienst neu starten (der Dienst pausiert nach einem Anmeldefehler 6 Stunden und versucht es nicht in Schleife).
|
||||
7. **Konten und Suchaufträge anlegen (von Hand).** Kleinanzeigen: Suchauftrag(e) mit Anzeigentyp „Gesuche“ (z. B. „Website erstellen“, „Homepage“, „Webdesigner“) und Benachrichtigung an die Alarm-Adresse; die Bestätigungs-Mail der Plattform anklicken. freelancermap und freelance.de: Suchagenten „Webdesign/Website/WordPress“ an dieselbe Adresse. Prüfung: Im Alarm-Postfach liegt je Plattform eine Bestätigungs- oder Test-Mail.
|
||||
8. **Erste echte Alarm-Mail abwarten.** Sobald sie da ist (höchstens 10 Minuten bis zum nächsten Lauf), in WOMS → Hustler → Kim anklicken: die Spur zeigt „Anzeigen gelesen“, der Raum „Kleinanzeigen“ ist nicht mehr grau. Steht dort „Alarm-Mail nicht lesbar“, leite die Mail (ohne persönliche Daten) weiter; sie ersetzt die Fixture aus Annahme A1/A2, und der Parser wird angepasst. Steht dort „Alarm-Mail ohne gültige Absender-Prüfung verworfen“, schick stattdessen nur die Kopfzeilen `From` und `Authentication-Results` der Mail (Gmail: Mail öffnen → „Original anzeigen“); fehlt `Authentication-Results` ganz, fügt dein Postfach-Anbieter die Zeile nicht ein, und die Prüfung muss für ihn angepasst werden. Nichts wird bis dahin ungeprüft übernommen.
|
||||
9. **nginx prüfen.** `curl -s -o /dev/null -w '%{http_code}\n' https://ticket.webklar.com/api/workers/pult` → `401`.
|
||||
10. **WOMS ausliefern.** Erst wenn P2 live ist: `git -C /home/webklar/apps/ticket.webklar.com/dev-workers push origin feature/workers:test`; nach etwa 3 Minuten in WOMS → Hustler → Reiter „Freigabe“: „Nichts wartet auf dich“.
|
||||
11. **Pult-Probe.** `docker exec webklar-workers node scripts/pult-test.mjs` druckt eine Id. In „Freigabe“ erscheint „[TEST] Pult-Probe – bitte verwerfen“. Prüfen: „Kopieren & öffnen“ öffnet webklar.com in neuem Tab und der Entwurf liegt in der Zwischenablage; „Ich kümmere mich“ zeigt deinen Namen; „Verwerfen“ (zweimal klicken) lässt den Eintrag verschwinden (Reiter „Erledigt“).
|
||||
12. **Datenschutz-Freigabe, dann Clara einschalten.** Erst wenn der Abschnitt „Interessenten“ in der Datenschutzerklärung freigegeben und in WOMS „Rechtliches“ hochgeladen ist (P6) und du den Hinweistext der Erstantwort (P3-T13, „Texte (exakt)“, Test `buildFinalText`) gelesen und freigegeben hast (die Adresse darin ist `https://www.webklar.com/datenschutz#interessenten`, sie muss auf der Live-Seite wirklich zum Abschnitt führen): `docker exec webklar-workers node scripts/datenschutz-schalter.mjs --an`. Es gibt genau diesen einen Schalter (`settings.privacyInteressentenLive`, kein Env); sobald P6 eingerichtet ist, nimmst du stattdessen `p6-freischalten.mjs datenschutz --check` und `--set`, das vorher die Live-Seite prüft und dasselbe Feld setzt (A5). Prüfung: Ausgabe `Datenschutz-Schalter: an`; innerhalb von 2 Minuten (Tick `anfragen`) entstehen Pult-Einträge für bereits bewertete Anfragen; ohne Anfragen bleibt „Freigabe“ leer (bei 0–3 Gesuchen pro Woche normal).
|
||||
13. **Rückweg.** Sofort stoppen: Pausenschalter in WOMS. Erstantworten abstellen: `docker exec webklar-workers node scripts/datenschutz-schalter.mjs --aus`. Alarm-Lesen abstellen: Raum „Kleinanzeigen“/„Portale“ in den Einstellungen deaktivieren. Alter Stand: `git -C /home/webklar/apps/workers checkout <vorheriger Commit>`, `docker compose build workers && docker compose up -d --no-deps workers`.
|
||||
### A. Voraussetzungen prüfen
|
||||
|
||||
**A1. P1 läuft.**
|
||||
```
|
||||
curl -s https://ticket.webklar.com/api/workers/health
|
||||
```
|
||||
*Erwartet:* `{"ok":true,"version":"0.1.0"}`. *Wenn nicht:* erst die Betriebsanleitung von P1 abarbeiten (Container, nginx-Block, Compose-Block), dann hier weitermachen.
|
||||
|
||||
**A1b. P1 ist vollständig.** Die Betriebsanleitung von P1 ist bis „Fertig, wenn: Schritte 7, 9, 10, 11, 12 erfüllt“ abgearbeitet. Für P3 zählt vor allem Schritt 9 (Startbestand): Appwrite-Konsole → Datenbank `woms-database` → Sammlung `workersKeys` hat Dokumente (Register der alten Leads und Kunden). *Erwartet:* Dokumente vorhanden. *Wenn nicht:* erst P1 Schritt 9 nachholen; sonst erkennt die Aufnahme bekannte Kunden und gesperrte Alt-Leads nicht als „schon bekannt“.
|
||||
|
||||
**A2. Produktions-Checkout und alter Stand.**
|
||||
```
|
||||
git -C /home/webklar/apps/workers status -sb | head -n1 && git -C /home/webklar/apps/workers rev-parse --short HEAD
|
||||
```
|
||||
*Erwartet:* `## main...origin/main` und eine Kurznummer; die Nummer notierst du als **ALT** (für den Rückweg in Block I). *Wenn nicht* (zum Beispiel `## HEAD (no branch)` nach einem früheren Rückweg): `git -C /home/webklar/apps/workers checkout main`, dann den Befehl wiederholen.
|
||||
|
||||
**A3. Genug Speicher.**
|
||||
```
|
||||
free -m | sed -n 2p
|
||||
```
|
||||
*Erwartet:* in der Spalte „available“ mindestens 2000 (MB). *Wenn nicht:* nicht bauen, erst Speicher freimachen (frag Claude).
|
||||
|
||||
**A4. Sicherungen.**
|
||||
```
|
||||
cd /home/webklar/apps && cp -p .env .env.bak-$(date +%F)-workers-p3 && cp -p docker-compose.yml docker-compose.yml.bak-$(date +%F)-workers-p3
|
||||
```
|
||||
*Erwartet:* keine Ausgabe; `ls -l .env.bak-*-workers-p3 docker-compose.yml.bak-*-workers-p3` zeigt zwei Dateien von heute. *Wenn nicht:* nichts ändern, bis beide da sind.
|
||||
|
||||
**A5. Kostengrenze steht.** Anthropic-Console → Workspace „Hustler“ → Limits: Ausgabenlimit **150 $ pro Monat** (aus P1). Das ist die harte Grenze für alles, was der Dienst ausgeben kann; der Dienst bremst sich selbst schon bei 6 $ am Tag und 150 $ im Monat. *Erwartet:* 150 $ steht dort. *Wenn nicht:* jetzt setzen, erst dann weiter.
|
||||
|
||||
### B. Dienst: Schema, Image, Neustart
|
||||
|
||||
**B1. Dienst-Stand übernehmen.**
|
||||
```
|
||||
git -C /home/webklar/apps/workers-dev push origin p3-anfragen:main && git -C /home/webklar/apps/workers pull --ff-only
|
||||
```
|
||||
*Erwartet:* kein Fehler; `git -C /home/webklar/apps/workers log --oneline -1` zeigt „alarme.js: Kopfkommentar nennt die neuen Meldungen (unerkannter Betreff, gekürzte Mail)“ (`b742e79`) oder neuer. Der `pull` aktualisiert auch die Rollen-Texte `prompts/*.md`; sie gelten sofort und brauchen später nie einen Neubau. *Wenn nicht:* nie `--force`; bei einer Ablehnung („non-fast-forward“) oder „Not possible to fast-forward“ Claude Bescheid sagen.
|
||||
|
||||
**B2. Neue Zeile im Compose-Block** (einmal; der Block aus P1 kennt `WORKERS_ALERT_AUTHSERV` noch nicht).
|
||||
```
|
||||
cd /home/webklar/apps && sed -i '/- WORKERS_ALERT_IMAP_PASS=/a\ - WORKERS_ALERT_AUTHSERV=${WORKERS_ALERT_AUTHSERV:-}' docker-compose.yml
|
||||
```
|
||||
*Erwartet:* keine Ausgabe; `grep -c WORKERS_ALERT_AUTHSERV /home/webklar/apps/docker-compose.yml` → `1` und `cd /home/webklar/apps && docker compose config -q` ohne Fehler. *Wenn nicht:* `0` = die Zeile `WORKERS_ALERT_IMAP_PASS` fehlt im Block (Block aus `/home/webklar/apps/workers/deploy/compose-workers.yml` neu einfügen wie in P1); `2` = Befehl lief doppelt, eine Zeile im Editor löschen.
|
||||
|
||||
**B3. Websuche-Modell prüfen** (Pflicht vor dem Neustart; wenn du das in P1 schon mit `Exit 0` gemacht hast, überspringen).
|
||||
```
|
||||
cd /home/webklar/apps/workers-dev && ( ANTHROPIC_API_KEY="$(sed -n 's/^WORKERS_ANTHROPIC_API_KEY=//p' /home/webklar/apps/.env | tail -n1)" timeout 120 node --max-old-space-size=1024 scripts/check-modelle.mjs; echo "Exit $?" )
|
||||
```
|
||||
*Erwartet:* Zeile `claude-haiku-5-5: kann web_search`, Schlusszeile `Haiku kann suchen: keine Änderung nötig.`, `Exit 0`. *Wenn nicht:* druckt das Skript die Zeile `WORKERS_SEARCH_MODEL=claude-sonnet-5-5`, hängst du sie an `/home/webklar/apps/.env` an (Rita und Finn suchen dann mit Sonnet, teurer); `Exit 2` = unklar, denselben Befehl mit `--probe` wiederholen (eine echte Probe-Suche, etwa 0,02 $); `Exit 1` = Suche mit diesen Modellen nicht möglich: B5 nicht ausführen, Claude Bescheid sagen.
|
||||
|
||||
**B4. Schema nachziehen** (vor dem Neustart, sonst scheitert das erste Anlegen der Einstellungen und später der Datenschutz-Schalter am unbekannten Feld). Der Laufzeit-Schlüssel des Dienstes darf das nicht (nur `documents.read/write`, nie erweitern). Lege dafür in der Appwrite-Konsole (Projekt `6a1058610003c5a13a05` → Integrationen → API-Schlüssel) einen Schlüssel `workers-setup` an: Ablauf 1 Tag, Bereiche `databases.read`, `collections.read/write`, `attributes.read/write`, `indexes.read/write`, **nie** `databases.write`, nicht in `.env`.
|
||||
```
|
||||
cd /home/webklar/apps/workers-dev && ( read -rs -p 'Appwrite-Key workers-setup: ' APPWRITE_API_KEY; echo; export APPWRITE_API_KEY; timeout 300 node --max-old-space-size=1024 scripts/setup-collections.mjs --check; echo "Exit $?" )
|
||||
```
|
||||
*Erwartet:* 13 Zeilen `= workers…: N Attribute, M Indizes vorhanden`, genau zwei Zeilen `- workersSettings.privacyInteressentenLive FEHLT` und `- Index workersMessages.idx_threadId FEHLT`, Schlusszeile `2 Eintrag/Einträge fehlen.`, `Exit 1`. Dann derselbe Befehl ohne `--check` (zwei Zeilen `+ Attribut …` und `+ Index …`, am Ende `Fertig.`), danach wieder mit `--check`: `Alles vorhanden.`, `Exit 0`. **Danach den Schlüssel `workers-setup` in der Appwrite-Konsole löschen.** *Wenn nicht:* Meldung mit 401 oder „missing scope“ = falscher Schlüssel (der Laufzeit-Schlüssel kann das nicht); mehr als zwei `FEHLT`-Zeilen = der Lauf ohne `--check` zieht alles nach (das Skript ist wiederholbar); `NICHT BEREIT` = ein paar Minuten warten und `--check` wiederholen.
|
||||
|
||||
**B5. Image bauen und Dienst neu starten.**
|
||||
```
|
||||
cd /home/webklar/apps && docker compose build workers && docker compose up -d --no-deps workers
|
||||
```
|
||||
*Erwartet:* der Bau läuft ohne Fehler durch (`npm ci` holt die neuen Pakete imapflow und mailparser), der Container wird neu erstellt. *Wenn nicht:* bei „Killed“ oder Speicherfehler siehe A3, nichts erzwingen; sonst die letzten Zeilen an Claude.
|
||||
|
||||
**B6. Läuft der neue Dienst?**
|
||||
```
|
||||
curl -s -o /dev/null -w '%{http_code}\n' https://ticket.webklar.com/api/workers/pult
|
||||
```
|
||||
*Erwartet:* `401` (ohne Anmeldung). Zusätzlich `docker logs --tail 40 webklar-workers`: eine Zeile mit `workers listening on 8092`, keine Zeile mit `Abbruch:` oder `Konfiguration`. *Wenn nicht:* `404` = der alte Dienst läuft noch (B5 wiederholen, `docker ps` prüfen); `Abbruch:`/`Konfiguration unvollständig` = ein Eintrag in `.env` fehlt oder ist kaputt (der Dienst startet dann in einer Schleife neu): Zeile an Claude, nichts raten.
|
||||
|
||||
**B7. Rollen-Texte im Container.**
|
||||
```
|
||||
docker exec webklar-workers grep -c '^Auftreten:' /app/prompts/rita.md /app/prompts/finn.md /app/prompts/bea.md /app/prompts/clara.md
|
||||
```
|
||||
*Erwartet:* vier Zeilen, jede endet auf `:1`. *Wenn nicht:* eine Datei fehlt oder ist noch der alte Entwurf: B1 prüfen; fehlt eine ganz, wirft die Rolle `Prompt fehlt: <Name>` beim ersten Aufruf.
|
||||
|
||||
**B8. Keine RSS-Feeds eingetragen** (optional). Pia hat einen RSS-Teil, dessen Feed-Liste absichtlich leer bleibt: der Dienst ruft dann keine fremden Adressen ab, und die noch offenen Sicherheitspunkte zu RSS-Weiterleitungen sind ohne Wirkung.
|
||||
```
|
||||
docker exec webklar-workers node -e "import(\"/app/src/anfragen/rss.js\").then(m=>console.log(m.RSS_FEEDS.length))"
|
||||
```
|
||||
*Erwartet:* `0`. *Wenn nicht:* eine andere Zahl heißt, dass ein Feed eingetragen ist: Claude Bescheid, bevor der Raum „Portale“ eingeschaltet wird.
|
||||
|
||||
### C. Alarm-Postfach (Kleinanzeigen, freelancermap, freelance.de)
|
||||
|
||||
Kim (Raum „Kleinanzeigen“) und Pia (Raum „Portale“) lesen ein Postfach, in das die Plattformen ihre Suchauftrags-Mails schicken. Der Dienst liest nur den Ordner Posteingang (INBOX), über IMAP mit Port 993 (verschlüsselt); er markiert, verschiebt und löscht nichts. Nimm ein eigenes Postfach nur für diese Mails mit eingeschaltetem Spamfilter; was im Spam-Ordner liegt, sieht der Dienst nicht.
|
||||
|
||||
**C1. Zugangsdaten eintragen.** Erst Host, Port und Benutzer (beide Werte in Anführungszeichen durch deine ersetzen):
|
||||
```
|
||||
printf 'WORKERS_ALERT_IMAP_HOST=%s\nWORKERS_ALERT_IMAP_PORT=993\nWORKERS_ALERT_IMAP_USER=%s\n' 'imap.beispiel.de' 'alarm@beispiel.de' >> /home/webklar/apps/.env
|
||||
```
|
||||
Dann das Passwort (bei Google Workspace ein App-Passwort, IMAP muss für das Postfach aktiv sein):
|
||||
```
|
||||
read -rs -p 'Passwort des Alarm-Postfachs: ' K; echo; printf 'WORKERS_ALERT_IMAP_PASS=%s\n' "$K" >> /home/webklar/apps/.env; unset K
|
||||
```
|
||||
*Erwartet:* `grep -c '^WORKERS_ALERT_IMAP_' /home/webklar/apps/.env` → `4`. *Wenn nicht:* mehr als 4 (frühere Versuche) = doppelte Zeilen im Editor löschen; weniger als 4 = fehlende Zeile ergänzen (alle vier oder keine, sonst bricht der Dienst beim Start ab).
|
||||
|
||||
**C2. Neu starten, Lesestand prüfen.**
|
||||
```
|
||||
cd /home/webklar/apps && docker compose up -d --no-deps workers
|
||||
```
|
||||
Nach etwa einer Minute: `docker exec webklar-workers cat /data/imap-state.json`. *Erwartet:* `{"alarme":{"uidValidity":…,"lastUid":…}}`. Der erste Lauf übernimmt nur den aktuellen Stand des Postfachs, Altmails werden nie gelesen; darum legst du Suchaufträge erst in C6 an. *Wenn nicht:* Datei fehlt = `docker logs --tail 40 webklar-workers`: `Alarm-Postfach: Anmeldung abgelehnt, Pause 6 Stunden` heißt falsche Zugangsdaten (weiter bei C3).
|
||||
|
||||
**C3. Postfach prüfen** (loggt sich genau einmal ein, liest nur).
|
||||
```
|
||||
docker exec webklar-workers node scripts/check-alert-mailbox.mjs
|
||||
```
|
||||
*Erwartet:* drei Zeilen: `[alarme] uidValidity=… uidNext=… exists=…`, `[alarme] Absender der letzten 10 Mails: …`, `[alarme] authserv-id der Authentication-Results der letzten 10 Mails: …`; nie Passwort, Betreff oder Text. `exists` sollte klein sein (der Dienst holt je Lauf höchstens 50 Mails). *Wenn nicht:* `[alarme] Fehler: IMAP_AUTH` = Passwort oder Benutzer falsch: in `.env` korrigieren (Sicherung aus A4 gilt), dann Neustart wie in C2 (der Neustart hebt auch die 6-Stunden-Pause des Dienstes auf; nicht in einer Schleife wiederholen, sonst sperrt der Anbieter das Konto); andere Fehler = Host oder Port falsch (nur Port 993 mit TLS).
|
||||
|
||||
**C4. Test-Mail senden, authserv-id ablesen.** Schick von einer fremden Adresse (zum Beispiel deinem Gmail) eine normale Test-Mail an die Alarm-Adresse, warte bis zu 10 Minuten, lies mit `docker exec webklar-workers cat /data/imap-state.json` die neue `lastUid` ab und wiederhole C3:
|
||||
```
|
||||
docker exec webklar-workers node scripts/check-alert-mailbox.mjs
|
||||
```
|
||||
*Erwartet:* `lastUid` ist um die Zahl der eingegangenen Mails höher als in C2 (bei einer Test-Mail genau 1: der Dienst holt sie und ignoriert sie still, weil sie von keiner Plattform kommt), und die dritte Zeile nennt einen Rechnernamen mit Anzahl, bei Google Workspace `mx.google.com 1`; den Namen (ohne die Zahl) merkst du dir. *Wenn nicht:* `(keine)` = dein Anbieter schreibt keine `Authentication-Results`-Zeile, dann würde jede Alarm-Mail als unecht verworfen (gewollt, die Prüfung wird nie abgeschwächt): nimm ein Google-Workspace-Postfach; `(unlesbar)` = Claude Bescheid; `lastUid` steigt nicht = `docker logs --since 15m webklar-workers | grep 'Alarm-Postfach'` an Claude.
|
||||
|
||||
**C5. Absender-Kennung festschreiben** (Pflicht, bevor du bei den Plattformen Suchaufträge anlegst: erst dann kommen echte Alarm-Mails, und die Räume „Kleinanzeigen“ und „Portale“ werden angeschlossen). Ohne den Wert würde eine vom Absender selbst geschriebene `Authentication-Results`-Zeile reichen, falls dein Anbieter keine schreibt. Wert aus C4 einsetzen:
|
||||
```
|
||||
printf 'WORKERS_ALERT_AUTHSERV=%s\n' 'mx.google.com' >> /home/webklar/apps/.env
|
||||
```
|
||||
Dann Neustart wie in C2. *Erwartet:* `docker exec webklar-workers printenv WORKERS_ALERT_AUTHSERV` druckt den Wert. *Wenn nicht:* leere Ausgabe = die Compose-Zeile aus B2 fehlt; `Konfiguration ungültig (unzulässige Zeichen): WORKERS_ALERT_AUTHSERV` = nur der Rechnername, ohne Leerzeichen, Semikolon oder Klammern. Ein falscher Wert verwirft später jede echte Alarm-Mail sichtbar als „Alarm-Mail ohne gültige Absender-Prüfung verworfen“ (dann C7).
|
||||
|
||||
Der Lesestand übersteht den Neustart:
|
||||
```
|
||||
docker exec webklar-workers cat /data/imap-state.json
|
||||
```
|
||||
*Erwartet:* dieselbe `lastUid` wie in C4 (oder höher, falls inzwischen Mails kamen); die Datei liegt im Volume `workers_data`. *Wenn nicht:* Datei fehlt oder zeigt eine Zahl, die nicht zu den eingegangenen Mails passt: Claude Bescheid, bevor du in C6 Suchaufträge anlegst.
|
||||
|
||||
**C6. Konten und Suchaufträge anlegen** (von Hand, erst jetzt). Kleinanzeigen: Suchauftrag mit Anzeigentyp „Gesuche“ (zum Beispiel „Website erstellen“, „Homepage“, „Webdesigner“), Benachrichtigung per E-Mail an die Alarm-Adresse, die Bestätigungs-Mail anklicken. freelancermap und freelance.de: Suchagent „Webdesign / Website / WordPress“ an dieselbe Adresse. Wähle Namen ohne die Wörter bestätig, aktivier, willkommen, registrier, passwort, zugangsdaten: Mails mit einem davon im Betreff gelten als Bestätigungs-Mails und werden nicht gelesen (steht der Name im Betreff in Anführungszeichen, schadet er nicht; verlass dich nicht darauf). Kein Befehl nötig. *Erwartet:* je Plattform eine Bestätigungs- oder Test-Mail im Posteingang. *Wenn nicht:* im Spam-Ordner nachsehen und dort „kein Spam“ markieren (nur der Posteingang wird gelesen).
|
||||
|
||||
**C7. Erste echte Alarm-Mail** (kann Stunden dauern; mach inzwischen D bis F weiter). Sobald eine Alarm-Mail im Posteingang liegt, holt der Dienst sie innerhalb von 10 Minuten. Kein Befehl; öffne in WOMS → Hustler → Hochhaus die Figur Kim (Pia für freelancermap und freelance.de) und lies die Spur. *Erwartet:* `1 Anzeige gelesen: Kleinanzeigen-Alarm` (bei mehreren `N Anzeigen gelesen: …`; bei Pia `freelancermap-Alarm` oder `freelance.de-Alarm`), je Anzeige `Neue Anfrage: <Titel> (PLZ Ort)`, und der Raum ist nicht mehr grau. Titel und Ort stimmen mit der Mail überein (kein Knopftext wie „Zum Projekt“ als Titel), die Zahl der Anfragen ist die Zahl der Anzeigen; bei Pia ergeben zwei Projekte aus derselben Stadt zwei Anfragen, und bei Arne (Etage „Keller“) steht keine Zeile „schon bekannt: <Titel>“ zu diesen Projekten. Im Webmail bleibt die Mail ungelesen (der Dienst markiert nichts). *Wenn nicht:* Raum nach 30 Minuten noch grau, obwohl die Mail im Posteingang liegt:
|
||||
- „Mail von <Plattform> ohne erkannten Alarm-Betreff ignoriert – Betreff-Muster prüfen?“ → gib Claude die Betreffzeile (nur die).
|
||||
- „Alarm-Mail nicht lesbar – Layout geändert?“ → leite die Mail ohne persönliche Daten an Claude weiter (sie ersetzt die nachgebaute Vorlage, der Parser wird angepasst).
|
||||
- „Alarm-Mail ohne gültige Absender-Prüfung verworfen“ → im Webmail „Original anzeigen“ (Gmail: Mail öffnen → drei Punkte) und nur die Kopfzeilen `From` und `Authentication-Results` an Claude geben. Die oberste `Authentication-Results`-Zeile muss mit dem Wert aus C5 beginnen und `dmarc=pass` mit `header.from=<Plattform-Domain>` (oder `dkim=pass` mit passendem `header.d`) enthalten.
|
||||
- gar keine Meldung → die Mail liegt im Spam statt im Posteingang (C6), ihr Betreff ist eine Bestätigungs-Mail (gewollt still), oder der Raum ist in den Einstellungen abgewählt.
|
||||
|
||||
Danach Beas Spur (Raum „Waage“): Nach 2 bis 6 Minuten steht dort „Anfrage bewertet: N von 100 Punkten, passt“ bzw. „passt nicht“. *Wenn nicht:* „Bewertung nach 3 Fehlversuchen aufgegeben“ → D3 wiederholen und die Zeile an Claude.
|
||||
|
||||
**C8. Fälschungsprobe** (nach C7; kann auch später). Eine Mail, die sich nur als Plattform ausgibt, muss verworfen werden. Ersetze `alarm@beispiel.de` durch deine Alarm-Adresse:
|
||||
```
|
||||
cat > /tmp/faelschung.eml <<'EOF'
|
||||
From: Kleinanzeigen <noreply@kleinanzeigen.de>
|
||||
To: alarm@beispiel.de
|
||||
Subject: 1 neue Anzeige fuer deinen Suchauftrag
|
||||
Authentication-Results: mx.test; dmarc=pass header.from=kleinanzeigen.de
|
||||
Content-Type: text/html; charset=utf-8
|
||||
|
||||
<p>Faelschungsprobe, bitte ignorieren.</p>
|
||||
EOF
|
||||
```
|
||||
Eine zweite Fassung mit doppelter `From`-Zeile:
|
||||
```
|
||||
sed '1i From: Kleinanzeigen <noreply@kleinanzeigen.de>' /tmp/faelschung.eml > /tmp/faelschung2.eml
|
||||
```
|
||||
Senden aus einem anderen Postfach (SMTP-Host und Adresse einsetzen; curl fragt nach dem Passwort), erst die erste, dann die zweite Datei:
|
||||
```
|
||||
curl --ssl-reqd --url 'smtp://smtp.beispiel.de:587' --user 'absender@beispiel.de' --mail-from 'absender@beispiel.de' --mail-rcpt 'alarm@beispiel.de' -T /tmp/faelschung.eml
|
||||
```
|
||||
*Erwartet:* innerhalb von 10 Minuten steht bei Kim zweimal „Alarm-Mail ohne gültige Absender-Prüfung verworfen“ und es entsteht keine neue Anfrage. Danach die Testmails im Postfach löschen und `rm -f /tmp/faelschung.eml /tmp/faelschung2.eml`. *Wenn nicht:* eine neue Anfrage entsteht = die Prüfung ist nicht scharf (Wert aus C5 fehlt oder ist falsch): Pause (I1) und sofort Claude Bescheid; kommt die Mail gar nicht an (dein Absender-Server lehnt den fremden Absender ab oder sie liegt im Spam), ist die Probe nicht möglich und kein Fehler; verlass dich dann auf C5.
|
||||
|
||||
### D. Websuche (Rita und Finn)
|
||||
|
||||
**D1. Such-Probe starten.** Rita (Raum „Netz-Radar“) und Finn (Raum „Foren & Fragen“) suchen ab jetzt jede Nacht von selbst (Otto schickt sie los); die Probe plant zusätzlich einen Lauf sofort (nur einmal ausführen).
|
||||
```
|
||||
docker exec webklar-workers node scripts/jetzt.mjs nachtplan
|
||||
```
|
||||
*Erwartet:* eine JSON-Zeile `{"planned":[{"room":"netz","workerId":"rita",…},{"room":"foren","workerId":"finn",…}],"skipped":null}`. Der Dienst arbeitet die Aufträge im 2-Minuten-Takt ab. *Wenn nicht:* `"skipped":"keine_raeume"` = die Räume sind noch nicht verbunden (eine Minute nach dem Neustart warten, dann nochmal); `"skipped":"pause"` = der Dienst ist angehalten (WOMS „Fortsetzen“); `"skipped":"budget"` = Tagesgrenze erreicht.
|
||||
|
||||
**D2. Ergebnis ansehen** (nach 2 bis 4 Minuten; die Seite dafür kommt mit Block E, sonst später nachholen). Kein Befehl; WOMS → Hustler → Hochhaus: Rita und Finn anklicken. *Erwartet:* je eine Zeile `N Quellen gelesen: <Suchanfrage>` (0 Funde sind normal), keine rote `fehler`-Zeile, „API heute“ steigt um etwa 0,10 bis 0,30 $ je Lauf. Ohne WOMS zählt dies: `docker logs --since 10m webklar-workers 2>&1 | grep -c fehlgeschlagen` → `0`. *Wenn nicht:* rote Zeile oder Zahl über 0 = Dienst anhalten (I1) und die Zeile an Claude geben. Meldet die rote Zeile einen API-Fehler zur Websuche, zu web_fetch oder zum Antwortformat (Haiku), hängst du `printf "WORKERS_SEARCH_MODEL=claude-sonnet-5-5\n" >> /home/webklar/apps/.env` an, startest wie in C2 neu (Sonnet ist teurer) und wiederholst die Probe aus D1 einmal.
|
||||
|
||||
**D3. Bewertungs-Probe für Bea** (kostet etwa 0,01 $; prüft, ob die Bewertung mit dem Modell überhaupt läuft).
|
||||
```
|
||||
docker exec -i webklar-workers node --input-type=module <<'JS'
|
||||
import { loadConfig } from './src/config.js'
|
||||
import { buildDeps } from './src/main.js'
|
||||
import { beaSystem, SCORE_SCHEMA, normalizeScore } from './src/anfragen/bea.js'
|
||||
const deps = buildDeps(loadConfig())
|
||||
const heute = new Date().toISOString().slice(0, 10)
|
||||
const r = await deps.claude.ask(deps, { workerId: 'bea', system: await beaSystem(), input: JSON.stringify({ heute, plattform: 'Kleinanzeigen', titel: 'Suche Webdesigner für Vereinsseite', text: 'Unser Sportverein sucht jemanden, der unsere alte Homepage neu macht.', ort: '66640 Namborn', datum: heute }), schema: SCORE_SCHEMA, effort: 'low', maxTokens: 800 })
|
||||
console.log(normalizeScore(r.data))
|
||||
process.exit(0)
|
||||
JS
|
||||
```
|
||||
*Erwartet:* ein Objekt `{ score: …, reason: '…', qualifies: true, blocked: null }` (Punkte meist über 60). *Wenn nicht:* ein Fehler (zum Beispiel HTTP 400 oder „Antwort war kein gültiges JSON“) heißt, dass jede echte Anfrage später als „Bewertung nach 3 Fehlversuchen aufgegeben“ endet: Block G nicht beginnen, Fehlerzeile an Claude.
|
||||
|
||||
### E. WOMS ausliefern (P2 und P3 zusammen)
|
||||
|
||||
**E1. [Claude] Zweig vorbereiten.** Der WOMS-Zweig `feature/workers` enthält P2 und P3; mit dem Push geht beides live. Sag Claude: „WOMS-Zweig mit origin/test zusammenführen und prüfen.“ Claude führt im Ordner `/home/webklar/apps/ticket.webklar.com/dev-workers` zuerst `git fetch origin` aus, notiert `git rev-parse origin/test` als **ALT-WOMS** (der heutige Live-Stand), führt dann `git merge origin/test` aus (nie `--force`), startet die Suite (`timeout 900 node --max-old-space-size=1024 --test --test-concurrency=2 tests/*.test.mjs tests/unit/*.test.mjs`) und den Vite-Build (`systemd-run --scope -p MemoryMax=1500M -p MemorySwapMax=0 --quiet timeout 600 npx vite build --outDir "$(mktemp -d)"`). *Erwartet:* Merge ohne offenen Konflikt, `# fail 0`, Build Exit 0. *Wenn nicht:* Claude löst Konflikte oder meldet sich; du pushst nichts, bevor alles grün ist.
|
||||
|
||||
**E2. Pushen.** Der Push liefert P2 mit aus. Vorher die Entscheidung zur Handy-Startansicht aus dem P2-Plan treffen (Abschnitt „Bekannte Einschränkung vor dem Livegang: Handy-Startansicht“: 1 so lassen [empfohlen], 2 ganzes Hochhaus mit Zoom 1, 3 Verschieben nur mit zwei Fingern); ohne Antwort bleibt es bei Wahl 1 (so lassen).
|
||||
```
|
||||
git -C /home/webklar/apps/ticket.webklar.com/dev-workers push origin feature/workers:test
|
||||
```
|
||||
*Erwartet:* Push angenommen; nach 1 bis 3 Minuten baut der Webhook. Prüfen: `curl -sI https://ticket.webklar.com/fonts/PixelifySans-Variable.woff2 | grep -iE '^(HTTP|content-type)'` → `200` und `font/woff2`. *Wenn nicht:* abgelehnt („non-fast-forward“) = `test` hat neue Commits, nie erzwingen, E1 wiederholen; `text/html` statt `font/woff2` = der Bau ist noch nicht durch, 2 Minuten warten.
|
||||
|
||||
**E3. Seite ansehen.** Kein Befehl; WOMS → Hustler. *Erwartet:* zwei Reiter „Hochhaus“ und „Freigabe“; „Freigabe“ zeigt „Nichts wartet auf dich“; im Hochhaus sind „Netz-Radar“ und „Foren & Fragen“ farbig, „Kleinanzeigen“ und „Portale“ bleiben grau („noch nicht angeschlossen“), bis die erste echte Alarm-Mail (C7) gelesen ist. Danach die P2-Abnahme nach dem P2-Plan (Menü, Hochhaus, Pausenschalter). *Wenn nicht:* „Das Pult ist gerade nicht erreichbar“ = Dienst oder nginx prüfen (B6); ein Reiter fehlt = Seite hart neu laden (Strg/Cmd+Umschalt+R).
|
||||
|
||||
### F. Abnahme im Browser
|
||||
|
||||
**F1. Pult-Probe anlegen** (geht genau einmal).
|
||||
```
|
||||
docker exec webklar-workers node scripts/pult-test.mjs
|
||||
```
|
||||
*Erwartet:* `Pult-Probe: Eintrag <Id> (neu angelegt)`; in „Freigabe“ (Reiter „Wartet“) steht „[TEST] Pult-Probe – bitte verwerfen“ mit dem Etikett „Antwort auf Plattform-Anfrage“. Ein zweiter Aufruf meldet `(schon vorhanden)` und legt nichts an. Bis zum Verwerfen zeigt Clara einmal am Tag den roten Hinweis „Clara wartet: Datenschutz-Abschnitt …“; das ist erwartet. *Wenn nicht:* `Fehler: …` an Claude geben; kein Eintrag sichtbar = einen Moment warten, Seite neu laden.
|
||||
|
||||
**F2. Kopieren & öffnen** (in Chrome, danach in Safari). Lege vorher einen anderen Text in die Zwischenablage und ändere im Entwurf einen Satz. Klick „Kopieren & öffnen“. *Erwartet:* ein neuer Tab mit webklar.com (kein Pop-up-Fenster), die Meldung „Kopiert und geöffnet.“, die Zeile zeigt „übernommen von <dein Name>“, und Einfügen in ein beliebiges Textfeld ergibt genau den geänderten Entwurf. *Wenn nicht:* „Geöffnet, aber das Kopieren hat nicht geklappt“ oder der alte Text kommt beim Einfügen = Browser und Meldung an Claude; kein Tab = Pop-ups für ticket.webklar.com erlauben.
|
||||
|
||||
**F3. Details.** Klick in der Zeile auf „Details“. *Erwartet:* ein Fenster (am Handy ein Blatt von unten) mit „Anfrage“ und „Nachrichtenverlauf: Noch keine Nachrichten.“, in unter 2 Sekunden. *Wenn nicht:* eine Fehlermeldung mit „Neu laden“ = B6 prüfen, dann Claude.
|
||||
|
||||
**F4. Kollege sieht Namen** (Mitarbeiter ohne Admin-Recht: Justin, Nico oder Andrej). Er öffnet „Freigabe“ in seinem Browser. *Erwartet:* „übernommen von Kenso“ (dein Name), nicht „Kollege“; klickt er „Kopieren & öffnen“, erscheint nach dem Kopieren der Hinweis „Wird schon von Kenso bearbeitet.“ *Wenn nicht:* „Kollege“ = die Mitarbeiterliste ist für diese Rolle nicht lesbar: Claude Bescheid (Rechte der Sammlung `employees`).
|
||||
|
||||
**F5. Handy** (375 Pixel breit, echtes Gerät oder Entwicklerwerkzeug). *Erwartet:* kein seitliches Scrollen, Knöpfe untereinander in voller Breite, lange Titel brechen um, der Entwurf ist bearbeitbar, „Details“ öffnet als Blatt von unten, das Etikett „Antwort auf Plattform-Anfrage“ passt. *Wenn nicht:* Bild und Gerät an Claude.
|
||||
|
||||
**F6. Netzausfall** (optional, 12 Minuten; prüft, dass ein kurzer Netzausfall die Seite nicht sperrt). Lass „Hochhaus“ offen, schalte am Rechner das WLAN aus, warte 11 Minuten (die Anmeldung für den Dienst gilt 10 Minuten), schalte es wieder ein. Kein Befehl. *Erwartet:* während des Ausfalls die Zeile „Verbindung gestört: Der Hustler-Dienst ist gerade nicht erreichbar. Es wird weiter versucht.“, nach dem Einschalten verschwindet sie binnen etwa 15 Sekunden von selbst. *Wenn nicht:* „Anmeldung abgelaufen. Bitte neu anmelden.“ länger als 15 Sekunden nach dem Einschalten = Claude Bescheid (Seite neu laden hilft sofort).
|
||||
|
||||
**F7. Verwerfen, Erledigt.** Klick „Verwerfen“, dann „Wirklich verwerfen?“ (zweiter Klick innerhalb von 4 Sekunden). *Erwartet:* der Eintrag verschwindet aus „Wartet“; im Reiter „Erledigt“ steht er mit dem Etikett „Verworfen“ und der Zeile „Verworfen von <Name> · gerade eben“; der Test-Interessent gilt als verloren. *Wenn nicht:* bleibt der Eintrag stehen, nennt die rote Meldung unter der Zeile den Grund: Claude Bescheid.
|
||||
|
||||
**F8. Pausenschalter und Rückfrage.** WOMS → Hustler → Hochhaus → „Alles anhalten“. *Erwartet:* ein Fenster „Alles anhalten?“ mit „Abbrechen“ und „Anhalten“; Esc oder „Abbrechen“ schließt es und ändert nichts (Statuszeile bleibt „Läuft“). Dann „Alles anhalten“ → „Anhalten“: Statuszeile „Angehalten: Es wird nichts gesendet und nichts gebaut.“; danach „Fortsetzen“ (ohne Rückfrage) → „Läuft“. Den Dienst nicht angehalten lassen. *Wenn nicht:* Meldung unter dem Knopf lesen, Seite neu laden, Claude.
|
||||
|
||||
### G. Clara einschalten
|
||||
|
||||
Am besten erst, wenn C7 gelaufen ist (eine echte Alarm-Mail wurde richtig gelesen) und D3 ohne Fehler durchging.
|
||||
|
||||
**G1. Wortlaut ansehen** (die Texte, die Clara anhängt; Anrede „Sie“ und „du“):
|
||||
```
|
||||
docker exec -i webklar-workers node --input-type=module <<'JS'
|
||||
import { buildFinalText } from './src/anfragen/clara.js'
|
||||
const angebot = 'Gratis-Vorschau, unverbindlich. Wenn sie gefällt, ist die Website im Hosting-Abo enthalten: 20 € im Monat, 12 Monate Mindestlaufzeit, ohne MwSt. nach § 19 UStG.'
|
||||
for (const tone of ['sie', 'du']) console.log(buildFinalText({ tone, body: '<freier Teil>', offerText: angebot, platform: 'Kleinanzeigen' }) + '\n-----')
|
||||
JS
|
||||
```
|
||||
*Erwartet:* zwei Texte: Anrede („Guten Tag,“ bzw. „Hallo,“), der freie Teil, der Angebotssatz, am Ende der Hinweis „Ich bin die KI-Assistentin von WEBklar (webklar.com) und antworte auf Ihre Anfrage auf Kleinanzeigen. … Hinweise zum Datenschutz: https://www.webklar.com/datenschutz#interessenten. … dann melden wir uns nicht wieder.“ Der Angebotssatz im echten Text kommt wörtlich aus WOMS → Hustler → Einstellungen → Angebot → „Angebotstext“ (jeder Admin kann ihn ändern): er muss der Satz aus der Spezifikation sein. Gib den Wortlaut frei oder sag Claude, was sich ändern soll (eine Änderung am Hinweis ist eine Code-Änderung mit neuem Image). *Wenn nicht:* Fehler beim Befehl = B5/B6 prüfen.
|
||||
|
||||
**G2. Datenschutz-Abschnitt ist online.** Der Abschnitt „Interessenten“ der Datenschutzerklärung ist freigegeben und in WOMS → Rechtliches hochgeladen, und er deckt die Aussagen aus dem Hinweis (Quelle der Angaben, Zweck „nur für diese Antwort“, Aufbewahrung 90 Tage, Widerspruch).
|
||||
```
|
||||
curl -sL https://www.webklar.com/datenschutz | grep -Ec 'id=.interessenten'
|
||||
```
|
||||
*Erwartet:* mindestens `1`. Zusätzlich im Browser `https://www.webklar.com/datenschutz#interessenten` öffnen: die Seite springt zum Abschnitt „Interessenten“. *Wenn nicht:* `0` = die Seite baut den Abschnitt eventuell erst im Browser auf; springt auch der Browser nicht zum Abschnitt, den Schalter in G4 nicht setzen und Claude Bescheid sagen (die Adresse steht in jeder Antwort).
|
||||
|
||||
**G3. Schalter steht auf „aus“.**
|
||||
```
|
||||
docker exec webklar-workers node scripts/datenschutz-schalter.mjs --status
|
||||
```
|
||||
*Erwartet:* `Datenschutz-Schalter: aus`. Solange er aus ist, schreibt Clara nichts; wartet ein bewerteter Interessent, meldet sie höchstens einmal am Tag rot „Clara wartet: Datenschutz-Abschnitt „Interessenten“ ist noch nicht freigegeben …“. *Wenn nicht:* `Datenschutz-Schalter: an` = er wurde schon gesetzt; prüfe G1 und G2 nachträglich und setze ihn im Zweifel mit `--aus` zurück (I2).
|
||||
|
||||
**G4. Einschalten.**
|
||||
```
|
||||
docker exec webklar-workers node scripts/datenschutz-schalter.mjs --an
|
||||
```
|
||||
*Erwartet:* `Datenschutz-Schalter: an`; innerhalb von 2 Minuten erscheinen in „Freigabe“ Entwürfe für schon bewertete Anfragen (ohne Anfragen bleibt der Reiter leer: bei 0 bis 3 Gesuchen pro Woche normal). Anfragen, die beim Einschalten älter als 14 Tage sind, bekommen keinen Entwurf (Spur Clara: „Anfrage älter als 14 Tage: nicht beantwortet“). Genau eine Option, sonst `Exit 2`. Ausschalten: `--aus`. Sobald P6 steht, nimmst du stattdessen `p6-freischalten.mjs datenschutz --check` und `--set`; es prüft vorher die Live-Seite und setzt dasselbe Feld. *Wenn nicht:* eine Fehlermeldung mit „Unknown attribute“ = B4; nach 5 Minuten kein Entwurf trotz bewerteter Anfrage = Spur von Clara und H2, dann Claude.
|
||||
|
||||
**G5. Die ersten fünf Entwürfe selbst lesen**, bevor du etwas sendest. Prüfe je Entwurf: keine Telefonnummer (in der Originalanfrage steht statt einer Nummer „[Nummer entfernt]“), keine Frist („in drei Tagen“), keine Webadresse außer der Datenschutz-Adresse, nur eine Begrüßung, der Angebotssatz wörtlich, der KI-Hinweis am Ende, und der freie Teil bezieht sich auf die Anfrage. „Anzeige öffnen“ führt auf die erwartete Plattform (`https://www.kleinanzeigen.de/s-anzeige/…`, `https://www.freelancermap.de/projekt/…`, `https://www.freelance.de/Projekte/…`, bei einem Fund der Websuche auf die Seite des Fundes) und zeigt dieselbe Anfrage. Kein Befehl. *Erwartet:* alle fünf sind in Ordnung; „(Vorlage)“ im Titel heißt, das Modell war zweimal unbrauchbar und die feste Vorlage kam (selten). *Wenn nicht:* Entwurf verwerfen und Claude den Fall zeigen; viele Vorlagen = Spur von Clara lesen und Claude fragen. Ein Eintrag mit dem Etikett „Mensch gewünscht“ („Von Hand antworten: …“) heißt, Clara konnte nicht schreiben: antworte selbst auf der Plattform, danach „Erledigt“.
|
||||
|
||||
**G6. Erste echte Antwort senden.** „Kopieren & öffnen“, in der Anzeige die Nachricht einfügen und abschicken, zurück in WOMS „Als gesendet markieren“. *Erwartet:* die Plattform nimmt die Nachricht mit dem Datenschutz-Link an (Kleinanzeigen kann externe Links filtern); der Eintrag steht in „Erledigt“ als „Gesendet von <Name>“; „Details“ zeigt im Nachrichtenverlauf deinen gesendeten Text (nicht den ersten Entwurf), der Interessent steht als „Kontaktiert“. *Wenn nicht:* die Plattform lehnt den Link ab = Claude Bescheid; „Details“ zeigt einen Fehler = `docker logs webklar-workers 2>&1 | grep 'API-Fehler (GET /api/workers/prospect'` und die Zeile an Claude (die Sortierung der Nachrichten ist erst mit der ersten gesendeten Antwort live prüfbar).
|
||||
|
||||
**G7. Antworten per E-Mail.** Einträge mit dem Etikett „Antwort per E-Mail“ haben den Knopf „E-Mail schreiben“; er öffnet das Mailprogramm deines Rechners mit dem dort eingestellten Konto (bei Entwürfen über 1800 Zeichen ohne Text). Schicke solche Antworten nur aus dem Postfach der neuen Absender-Domain (kommt mit P4), nie aus einem webklar.com-Postfach. Solange es sie nicht gibt: Eintrag liegen lassen (er bleibt unter „Wartet“) oder verwerfen. Vorher prüfen: die Adresse im Mailprogramm steht im Text der Anzeige („Anzeige öffnen“); sonst verwerfen. `WORKERS_AUTOMAIL` gibt es erst mit P4 (ja/nein, Standard nein): trag nichts davon in `.env` ein; der Dienst schickt in P3 nie selbst eine Mail an Interessenten. *Erwartet:* kein Mailversand ohne dein Zutun. *Wenn nicht:* ist trotzdem eine Mail an einen Interessenten gegangen, I1 und Claude.
|
||||
|
||||
### H. Erste Woche beobachten
|
||||
|
||||
**H1. Kosten** (jeden Morgen). WOMS → Hochhaus, Kennzahl „API heute“ (darunter „von 6,00 $“). Monat: Anthropic-Console → Workspace „Hustler“ → Nutzung. *Erwartet:* unter 1 $ je Nacht (Rita, Finn, einige Bea-Läufe), Tag weit unter 6 $, Monat weit unter 150 $. *Wenn nicht:* deutlich mehr (über 1 $ in einer Nacht) = Pause (I1) und Claude fragen.
|
||||
|
||||
**H2. Logs** (täglich).
|
||||
```
|
||||
docker logs --since 24h webklar-workers 2>&1 | grep -E 'Fehler bei|fehlgeschlagen|Alarm-Postfach|Tick abgebrochen|Unbehandelte Ablehnung|Prompt (fehlt|leer)'
|
||||
```
|
||||
*Erwartet:* keine Zeile (Zugangsdaten werden im Log geschwärzt). *Wenn nicht:* Zeilen an Claude; `Anmeldung abgelehnt, Pause 6 Stunden` = Passwort des Alarm-Postfachs geändert (C3). Ein hängender Anthropic-Aufruf kann den Takt bis zu etwa 15 Minuten aufhalten; das verzögert nur, du musst nichts tun.
|
||||
|
||||
**H3. Meldungen in der Spur von Kim, Pia, Rita, Finn, Bea und Clara und was sie heißen.** Kein Befehl. *Erwartet:* selten eine davon.
|
||||
- „Alarm-Mail ohne gültige Absender-Prüfung verworfen“ → C7.
|
||||
- „Mail von <Plattform> ohne erkannten Alarm-Betreff ignoriert – Betreff-Muster prüfen?“ (höchstens einmal je Plattform und Tag) → Betreff an Claude.
|
||||
- „Alarm-Mail nicht lesbar – Layout geändert?“ → Mail an Claude.
|
||||
- „Alarm-Mail von <Plattform> gekürzt: …“ → nur ein Hinweis, die Mail wurde gelesen; kommt es oft, den Suchauftrag enger fassen.
|
||||
- „Alarm-Postfach: Anmeldung fehlgeschlagen, Pause 6 Stunden“ → C3.
|
||||
- „Alarm-Postfach: Mail UID … nicht lesbar“ → eine einzelne Mail ließ sich nicht zerlegen; sie wird übersprungen und der Lesestand rückt vor. Kommt es wiederholt vor, Claude Bescheid.
|
||||
- „Alarm-Postfach: Mail UID … übersprungen“ → eine kaputte Mail wurde nach drei Versuchen übersprungen, Claude Bescheid.
|
||||
- „Bewertung nach 3 Fehlversuchen aufgegeben“ (Bea) → die Bewertung ist dreimal gescheitert, der Interessent gilt als verloren; kommt es mehrfach vor, D3 wiederholen und die Zeile an Claude geben.
|
||||
- „Clara konnte keine Antwort schreiben (Modell und Vorlage gescheitert) …“ → im Pult liegt „Von Hand antworten: …“, du antwortest selbst.
|
||||
- „Clara kann nicht antworten: Der Angebotstext in den Einstellungen ist leer“ (Clara meldet das bei jedem Lauf wieder) → in WOMS → Hustler → Einstellungen → Angebot den „Angebotstext“ wieder eintragen (Wortlaut siehe G1).
|
||||
- „Anfrage älter als 14 Tage: nicht beantwortet“ und „Adresse gesperrt: Anfrage wird nicht beantwortet“ → normal.
|
||||
|
||||
*Wenn nicht:* eine Meldung, die hier fehlt, mit Wortlaut an Claude.
|
||||
|
||||
**H4. Postfach und Speicher** (einmal pro Woche).
|
||||
```
|
||||
docker exec webklar-workers node scripts/check-alert-mailbox.mjs
|
||||
```
|
||||
*Erwartet:* `exists` klein; `docker stats --no-stream webklar-workers` zeigt weit unter 768 MiB. *Wenn nicht:* tausende Mails im Alarm-Postfach = aufräumen (Spam, fremde Mails), der Dienst arbeitet je Lauf nur 50 ab. Wichtig: Der Dienst setzt bei jedem Interessenten eine Löschfrist von 90 Tagen, **das Löschen selbst kommt aber erst mit P6**; lege deshalb im Alarm-Postfach eine Regel an, die Mails nach 90 Tagen löscht (der Dienst löscht dort nie), und erinnere Claude vor dem 90. Tag nach dem ersten Fund an P6.
|
||||
|
||||
**H5. Veraltet-Lauf** (ab dem dritten bis vierten Morgen nach dem ersten Entwurf, nach 07:00 Uhr Berlin: der tägliche Lauf setzt Einträge ab 3 Tagen Alter auf veraltet). Kein Befehl; „Freigabe“ → Reiter „Veraltet“. *Erwartet:* Antworten auf Plattform-Anfragen, die 3 Tage liegen, stehen dort und sind weiter zu übernehmen und zu senden; ein Eintrag aus „Erledigt“ taucht nie dort auf. *Wenn nicht:* ein bereits gesendeter Eintrag steht unter „Veraltet“ = Claude Bescheid. Offen für Claude, nicht für dich: Claude bestätigt an den ersten echten Alarm-Mails die nachgebauten Mail-Layouts und prüft an einer echten Suche, dass per `robots.txt` gesperrte Seiten ausgelassen werden.
|
||||
|
||||
### I. Rückweg
|
||||
|
||||
**I1. Sofort stoppen** (verliert nichts). WOMS → Hustler → Hochhaus → „Alles anhalten“ → „Anhalten“. Lesen, Suchen, Bewerten und Antworten-Schreiben stehen dann; Alarm-Mails bleiben im Postfach und werden nach „Fortsetzen“ gelesen; das Freigabe-Pult bleibt für dich bedienbar. *Erwartet:* Statuszeile „Angehalten: Es wird nichts gesendet und nichts gebaut.“ *Wenn nicht:* Meldung unter dem Knopf lesen, Seite neu laden, Claude.
|
||||
|
||||
**I2. Erstantworten aus.**
|
||||
```
|
||||
docker exec webklar-workers node scripts/datenschutz-schalter.mjs --aus
|
||||
```
|
||||
*Erwartet:* `Datenschutz-Schalter: aus`; schon angelegte Pult-Einträge bleiben stehen, neue entstehen nicht. *Wenn nicht:* Fehlermeldung an Claude.
|
||||
|
||||
**I3. Suche aus.** WOMS → Hustler → Hochhaus → „Einstellungen“ (Admin) → Gruppe „Räume“: „Netz-Radar“ und „Foren & Fragen“ abwählen, speichern. *Erwartet:* Rita und Finn suchen nicht mehr (geplante Aufträge enden ohne Suche). *Wenn nicht:* Dialog zeigt den Grund; ohne Admin-Recht fehlt der Knopf.
|
||||
|
||||
**I4. Alarm-Lesen dauerhaft aus.** Nicht die Räume „Kleinanzeigen“ und „Portale“ abwählen: der Dienst läse dann weiter, würfe die Mails weg und rückte den Lesestand vor, die Alarme gingen verloren. Stattdessen die IMAP-Zeilen entfernen:
|
||||
```
|
||||
cd /home/webklar/apps && sed -i '/^WORKERS_ALERT_IMAP_/d;/^WORKERS_ALERT_AUTHSERV=/d' .env
|
||||
```
|
||||
Dann Neustart wie in C2. *Erwartet:* `grep -c '^WORKERS_ALERT' .env` → `0`; die Räume „Kleinanzeigen“ und „Portale“ werden wieder grau. *Wenn nicht:* Zeilen übrig = von Hand im Editor löschen (Sicherung aus A4).
|
||||
|
||||
**I5. Alter Dienst-Stand.** Mit der Nummer ALT aus A2:
|
||||
```
|
||||
git -C /home/webklar/apps/workers checkout --detach ALT
|
||||
```
|
||||
Dann `cd /home/webklar/apps && docker compose build workers && docker compose up -d --no-deps workers`. *Erwartet:* `curl -s https://ticket.webklar.com/api/workers/health` antwortet wie in A1. Vor dem nächsten Update: `git -C /home/webklar/apps/workers checkout main`, sonst scheitert `git pull --ff-only`. Die in B4 angelegten Felder bleiben, sie schaden nicht. Nach dem Rückweg zeigt der Reiter „Freigabe“ „Das Pult ist gerade nicht erreichbar“, weil der alte Dienst keine Pult-Routen kennt (404); ohne I6 bleibt die Seite sonst ohne Folgen. *Wenn nicht:* Bau-Fehler an Claude.
|
||||
|
||||
**I6. WOMS zurücknehmen** (nur falls nötig). Wie in P2 Schritt 7 mit **ALT-WOMS** aus E1: Claude führt `git revert --no-edit $(git rev-list --no-merges ALT-WOMS..HEAD)` im Ordner `/home/webklar/apps/ticket.webklar.com/dev-workers` aus, du pushst `git -C /home/webklar/apps/ticket.webklar.com/dev-workers push origin feature/workers:test`. *Erwartet:* Weil P2 und P3 mit einem Push live gingen (E1), nimmt dieser Rückweg beide zurück: das Menü zeigt wieder das graue „Leads“ und `https://ticket.webklar.com/leads` zeigt wieder die Tickets (wie P2 Schritt 7); war P2 vorher schon live, zeigt „Hustler“ wieder den P2-Stand ohne Reiter „Freigabe“. Soll nur der Betrieb stoppen, genügen I1 bis I4. *Wenn nicht:* Claude Bescheid.
|
||||
Reference in new issue
Block a user