markemacht/dev/klarheits-check/UMSETZUNGSPLAN.md

16 KiB
Raw Blame History

Umsetzungsplan — Marken-Klarheits-Check

Kontext: Inbound-Magnet aus dem Konzept (marken-klarheits-check.md). 5 Fragen mit 4-Stufen-Skala + optionalem Freitext, deterministischer Reifegrad im Frontend, optionale KI-Auswertung über einen serverseitigen Laravel-Proxy (Anthropic). Referenz für Fragen/Skalen/Prompt/JSON-Vertrag bleibt Klarheits-Check_Prototyp.html.

Lebendes Dokument. Nach jedem Checkpoint aktualisieren. Wiederaufnahme: zuerst 📍 Aktueller Stand lesen, dann beim ersten offenen [ ]-Checkpoint weiterarbeiten. Jeder Checkpoint endet konsistent (Tests grün, nichts halb-fertig).


📍 Aktueller Stand (immer zuerst lesen)

  • Phase: ✅ 1–4 fertig, Phase 5 fast durch (CP 5.1/5.2/5.3 erledigt) + Prompt-Engineering eingebaut. Offen: nur noch CP 5.4 — Ton-Kalibrierung mit echten Keys + npm run build + manuelle Prüfung beider Modi/mobil. Keys liegen in .env, config/services.php ist eingerichtet (anthropic + openai).
  • Zuletzt erledigt: Phase 3 — KlarheitsCheckController@auswerten + Route POST /klarheits-check/auswerten (Name klarheits-check.auswerten). Validierung (5×{skala:0–3, text:nullable|max:5000}), Rate-Limit 10/IP/10min → 429. Anthropic-Call via Laravels Http nur bei Freitext, Modell/Key/Timeout aus config('services.anthropic.*'), System-Prompt aus config('klarheits_check.ki.system'). JSON geparst + auf den Vertrag normalisiert; jeder Fehler (kein Key, Timeout, Fehlerstatus, kaputtes JSON) → deterministischer Fallback. Keine Persistenz. 6 Http::fake()-Tests. 71/71 Tests grün, Pint sauber.
  • Als Nächstes: CP 5.4 — Testreihe via php artisan klarheits-check:probe (Default OpenAI) durchspielen, Ton in config('klarheits_check.ki.system') nachschärfen, dann npm run build + manuelle Prüfung beider Wirkungs-Modi/mobil. Legaltext in CP 4.2 bitte gegenprüfen (Anbieterwahl, SCC/DPA).
  • Branch: feat/markemacht-laravel-migration.
  • Modell bestätigt (via claude-api-Skill): claude-sonnet-4-6, kein Datums-Suffix; Header x-api-key + anthropic-version: 2023-06-01. Tests laufen ohne echten Key über Http::fake().
  • Dual-Anbieter: umschaltbar über KLARHEITS_CHECK_KI (anthropic | openai) → config('klarheits_check.ki.provider'). OpenAI via Chat Completions (config('services.openai.*'), response_format: json_object, max_completion_tokens). Beide Anbieter teilen System-Prompt + JSON-Vertrag; identischer Fallback. 72/72 Tests grün.

Gesperrte Entscheidungen (vom User bestätigt)

  • Ort/URL: eigene, teilbare Seite /klarheits-check (LinkedIn-Magnet, Umami, SEO).
  • Trigger: vertikaler Hochkant-Tab rechts (fix an der Bildschirmkante), verlinkt direkt auf /klarheits-check (springt zur Seite — Overlay wurde verworfen, User-Entscheidung). Auf Mobil horizontaler Pill unten rechts (gedrehte Tabs sind dort unpraktisch).
  • Reuse: Die Check-Markup-Quelle liegt als Blade-Partial vor (web.partials.klarheits-check), bleibt also wiederverwendbar, falls später doch ein Overlay o. Ä. gewünscht wird.
  • Kein Livewire: clientseitiger Quiz-State + ein einziger KI-Call → schlankes JS/Alpine + Controller-Endpoint. (Anders als Audit-Formular — bewusst, gemäß Konzept.)
  • KI nur bei Freitext: ohne Freitext kein Anthropic-Call (Datensparsamkeit). Deterministisches Ergebnis greift immer; KI schärft nur.
  • HTTP-Weg: Laravels Http-Client (keine neue Composer-Abhängigkeit; ein POST genügt).
  • Modell: Sonnet (gemäß Konzept; günstig für ein kostenloses, reichweitenstarkes Tool). Exakte Modell-ID beim Backend-Bau (CP 3.x) via claude-api-Skill bestätigen — Prototyp nennt claude-sonnet-4-6; keine Datums-Suffixe anhängen.
  • API-Key: ANTHROPIC_API_KEY in .env (Server + lokal zum Echt-Testen). Erst ab Phase 3 nötig; Tests nutzen einen gemockten HTTP-Client.

Fortschritt (Überblick)

Phase 1 — Frontend (Check-Seite, ohne KI) · braucht keinen Key ✅

  • CP 1.1 Route /klarheits-check (routes/marketing.php) + Seite web.klarheits-check im Marketing-Layout
  • CP 1.2 Check-Partial web.partials.klarheits-check aus config/klarheits_check.php; scoped CSS kc-* (Tokens)
  • CP 1.3 Quiz-JS resources/js/web/klarheits-check.js (Vite-Input + Layout-@vite); Fetch+Fallback; CSRF-Meta im Layout
  • CP 1.4 Pest KlarheitsCheckTest (Seite rendert, 20 Optionen, JS/CSRF) — 63/63 grün, Build erzeugt

Phase 2 — Hochkant-Tab ✅ (Overlay verworfen: Tab springt direkt zur Seite)

  • CP 2.1 Vertikaler Tab rechts im Marketing-Layout (global), writing-mode: vertical-rl, mobil als horizontaler Pill unten rechts; auf /klarheits-check selbst via @unless ausgeblendet
  • Overlay/Drawer — entfällt: Tab ist ein <a> auf route('klarheits-check') (User-Entscheidung)
  • Test: Tab vorhanden auf index, fehlt auf der Check-Seite

Phase 3 — Backend KI-Proxy ✅

Umgesetzt. app/Http/Controllers/Web/KlarheitsCheckController.php (auswerten), Route in routes/marketing.php. System-Prompt + max_tokens liegen in config/klarheits_check.php (ki.*) als einzige Wahrheit neben Fragen/Reifegrad. gradLine() serverseitig gespiegelt zum Frontend.

  • CP 3.1 KlarheitsCheckController@auswerten + Route POST /klarheits-check/auswerten (klarheits-check.auswerten). Validierung antworten = 5×{skala:0–3 (required), text:nullable|max:5000}. Rate-Limit 10/IP/10min → 429 (mit deterministischem Body). Deterministisches Ergebnis als Baseline.
  • CP 3.2 KI-Call nur bei Freitext, Anbieter via config('klarheits_check.ki.provider') (KLARHEITS_CHECK_KI): Anthropic (Http::withHeaders x-api-key/anthropic-version, config('services.anthropic.*')) oder OpenAI (Http::withToken, config('services.openai.url'), Chat Completions mit response_format: json_object + max_completion_tokens). Beide teilen System-Prompt (config('klarheits_check.ki.system')) und Vertrag — anthropicText()/openaiText().
  • CP 3.3 Antwort-Text aus content[] extrahiert, ```json-Zaun entfernt, json_decode, auf den Vertrag normalisiert (vertragNormalisieren): gesamt,spiegel:[{nr,text}],luecken[],stimmigkeit[],schluss.
  • CP 3.4 Timeout aus Config + try/catch (Throwable) + leerer Key → null → deterministischer Fallback (Tool funktioniert immer). Keine Persistenz.
  • CP 3.5 tests/Feature/Web/KlarheitsCheckAuswertenTest.php — 6 Http::fake()-Tests: Erfolg (+ Modell/ Header/System-Prompt geprüft), kaputtes JSON → Fallback, Fehlerstatus → Fallback, kein Freitext → kein KI-Call, Validierung (422), Rate-Limit (429). 71/71 grün.

Phase 4 — Datenschutz ✅

  • CP 4.1 Hinweis .kc-privacy in web.partials.klarheits-check (über dem „Jetzt auswerten"-Button): Freitext → KI-Dienst (Anthropic bzw. OpenAI, USA), keine Speicherung, ohne Text rein lokal; Link auf datenschutz#ki-auswertung.
  • CP 4.2 Datenschutz-Absatz „6. Marken-Klarheits-Check (KI-Auswertung)" (Anker #ki-auswertung) in web.datenschutz: nur Skala = lokal; nur bei Freitext Übermittlung; weder Name/E-Mail/IP; keine Speicherung; kein Training; Rechtsgrundlage Art. 6 (1) a, US-Transfer via Standardvertragsklauseln. 2 neue Tests. 74/74 grün.

Phase 5 — Tests, Ton, Messung, Build

  • CP 5.1 Pest: Routentests, Controller (gemockter HTTP-Client: Erfolg, KI-Ausfall→Fallback, Validierung, Rate-Limit)
  • CP 5.2 Pint, Gesamt-Suite grün
  • CP 5.3 Umami-Events in klarheits-check.js (Tracker self-hosted, im Marketing-Layout geladen): klarheits-check-gestartet (erste Auswahl), klarheits-check-abgeschlossen ({scharf,unscharf,offen,mit_text,ki} — Reifegrad-Verteilung + Flags, kein Freitext), klarheits-check-audit-klick. window.umami-Guard → no-op ohne Tracker.
  • CP 5.4 Ton-Kalibrierung (Testreihe inkl. Floskeln), npm run build, manuelle Prüfung beide Modi/mobil

Später (separat, nur notiert)

  • Karussell-Ableitung (Hebel B), A/B der Fragentexte, EU-Datenresidenz via Bedrock/Vertex falls gefordert.

Architektur-Notizen

  • Datenfluss: Browser sammelt 5 Skalen + optionale Texte → deterministischer Reifegrad lokal → bei Freitext: POST /klarheits-check/auswerten (eigene Domain, CSRF) → Controller baut Prompt, ruft Anthropic serverseitig, validiert JSON → zurück an Browser → Render. Key verlässt nie den Server.
  • HTTP-Weg (gesperrt): Laravels Http-Client, keine neue Composer-Abhängigkeit.
  • Modell (gesperrt): Sonnet. Finale Modell-ID beim Bau via claude-api-Skill bestätigen (config('services.anthropic.model'), Default claude-sonnet-4-6) — keine Datums-Suffixe.
  • Sicherheit: Prototyp ruft Anthropic direkt aus dem Browser (Zeile 186–189) — wird durch den serverseitigen Proxy ersetzt; der Key verlässt nie den Server.

Changelog (Arbeitsprotokoll)

Datum Checkpoint Ergebnis / Notiz
2026-06-18 Setup Plan angelegt. UX gesperrt: eigene Seite /klarheits-check + Hochkant-Tab rechts → Overlay. HTTP-Weg=Http-Client, Modell=Sonnet gesperrt.
2026-06-18 Phase 1 config/klarheits_check.php (Fragenkatalog), Seite + Partial + kc-*-CSS-Tokens, Quiz-JS (Fetch+Fallback), CSRF-Meta. Tests 63/63, Build ok.
2026-06-18 Phase 1 · UX Geführte Navigation: „Check starten"-Button → Frage 1; pro Frage „Weiter"-Button (erscheint nach Auswahl); Freitextfeld nach Auswahl dezent magenta hervorgehoben (ohne Fokus); Submit → „Jetzt auswerten"; alert ersetzt durch Inline-Fehler mit „Zur offenen Frage"-Sprung. Tests 64/64.
2026-06-18 Phase 1 · UX2 „Check starten" als Anker #check in den Hero verschoben (raus aus Partial); im Ergebnis-Zustand nur noch „Neu starten" (Start-Link + „Jetzt auswerten" ausgeblendet). Tests 64/64.
2026-06-18 Phase 1 · UX3 Start-Link scrollt sanft (JS); [data-kc-submit]/[data-kc-start-link] explizit hidden (sonst überstimmt .btn); Hint-Text + .kc-actions-Linie entfernt; „Neu starten" hinter den Audit-CTA verschoben. Tests 64/64.
2026-06-18 Phase 2 Hochkant-Tab rechts (.kc-tab, vertikal; mobil Pill unten rechts), Link auf /klarheits-check, auf der Seite selbst ausgeblendet. Overlay verworfen. Tests 65/65, Build ok.
2026-06-19 Phase 3 KI-Proxy: KlarheitsCheckController@auswerten + POST /klarheits-check/auswerten. Validierung, Rate-Limit 10/IP/10min, Anthropic-Call (Http, nur bei Freitext) via config('services.anthropic.*') + System-Prompt aus config('klarheits_check.ki.system'), JSON→Vertrag normalisiert, jeder Fehler → deterministischer Fallback, keine Persistenz. Modell claude-sonnet-4-6 via claude-api-Skill bestätigt. 6 Http::fake()-Tests. 71/71 grün, Pint sauber.
2026-06-19 Phase 1 · UX4 Ergebnis-Hinweis: ohne Freitext zeigt das Ergebnis Eyebrow „Statische Auswertung" + Callout .kc-r-hint („Möchten Sie mehr Klarheit und eine präzise Auswertung? … Felder ausfüllen"). Mit Freitext Eyebrow „Ihre Auswertung", kein Callout. Rein clientseitig (render() in klarheits-check.js), CSS-Token. npm run build nötig.
2026-06-19 Phase 5 · CP 5.3 Umami-Events in klarheits-check.js (Tracker self-hosted, Layout Z. 60): klarheits-check-gestartet (erste Auswahl, einmalig), klarheits-check-abgeschlossen ({scharf,unscharf,offen,mit_text,ki}, kein Freitext), klarheits-check-audit-klick. window.umami-Guard → no-op. npm run build nötig.
2026-06-19 Belegter Grad Designfehler behoben: erste Spalte zeigte den Skalen-Grad, der Spiegeltext den belegten — Widerspruch (SCHARF über „nicht prüfbar"). Vertrag um spiegel[].grad (scharf/unscharf/nicht entschieden) erweitert: Validierung als Pflichtfeld (fehlt/ungültig → Fallback), Frontend liest grad für Label+Farbe statt der Skala, Skala-Auswahl raus aus der Stufen-Spalte. Fallback-Ehrlichkeit: Freitext ohne belegten Grad (KI aus) zeigt Skala-Stufe + Hinweis „Wortlaut-Prüfung nicht verfügbar" (.kc-r-unverif); ohne Freitext unverändert. +2 Tests. 82/82 grün. npm run build nötig.
2026-06-19 Diagnose · Leerzeichen Fehlende Leerzeichen an Wortgrenzen (Opus + gpt-5.5) als Modell-Token-Grenzen identifiziert, nicht code-seitig: kein Streaming, strtr-Normalisierung fasst keinen Whitespace an, Konsolen-Print erhält Leerzeichen (empirisch bestätigt); gpt-5.5 reproduzierte denselben verbatim-Schluss korrekt, Opus nicht — gleicher Code-Pfad. Sicherheitsnetz: kanonischerSchluss() schnappt eine KI-schluss, die bis auf Whitespace einem der vier Tier-Texte gleicht, auf den exakten Config-Text. Freitext-Felder (spiegel/luecken/gesamt-Nuance) bleiben modellseitig. +1 Test. 81/81 grün.
2026-06-19 Prompt v2 · Konsistenz Nach erster Auswertung (User-Prompt überarbeitet, max_tokens=4096): (1) Nutzernachricht liefert pro Frage Grad (Baustein/Skalenwert/Grad/Freitext) mit. (2) Vier kanonische Stufen-Texte als config('klarheits_check.stufen') = eine Wahrheit für Prompt <gesamturteil>, Service-Fallback und JS-No-Text-Pfad (STUFEN/stufeFor); Gesamtstufe aus Anzahl „scharf"-Grade (>=4/2–3/sonst). Alte widersprüchliche Texte (Schärfe ist da / hält das) entfernt. (3) geradeAnfuehrung() normalisiert typografische → gerade Anführungszeichen nach dem Parsen (Sicherheitsnetz gpt-5.4-mini). +2 Tests (Grad in Nutzernachricht, Quote-Normalisierung). 79/79 grün.
2026-06-19 CP 5.4 · Werkzeug Auswertung in App\Services\KlarheitsCheckAuswerter extrahiert (Controller delegiert; Verhalten unverändert, 78/78). Neues Artisan-Command klarheits-check:probe {--provider=openai} {--only=} spielt 4 Szenarien (floskeln, scharf, widerspruch, offtopic/Injection) gegen den Anbieter und zeigt das JSON — zur Ton-Kalibrierung. 3 gemockte Command-Tests.
2026-06-19 Prompt-Engineering System-Prompt auf die kuratierte Version aus klarheits-promt.md umgestellt (XML-Tags: <haltung>/<grundsatz>/<pruefregeln>/<floskeln>/<abgleich>/<zuschreibung>/<aufgabe>/<beispiele>, Urteilsregeln je Baustein, Few-Shot). Neu: <relevanz>-Block = Themen-/Missbrauchs-Prüfung + Prompt-Injection-Schutz (Anweisungen im Freitext werden ignoriert, Prompt nie ausgegeben). JSON-Vertrag unverändert → kein Frontend-Eingriff. +1 Config-Test. 75/75 grün.
2026-06-19 Phase 4 · Privacy-Modus KI-Anfrage im Privacy-Modus: OpenAI-Call mit store=false (keine Speicherung der Completion); Anthropic ohne Request-Flag (API-Bedingung „kein Training"). In .kc-privacy + Datenschutz-Abschnitt dokumentiert. Test prüft store=false. 74/74 grün.
2026-06-19 Phase 4 Datenschutz: Hinweis .kc-privacy am Auswerten-Punkt (Freitext → Anthropic/OpenAI, USA, keine Speicherung, sonst lokal) + Link auf datenschutz#ki-auswertung; neue Datenschutz-Sektion „6. KI-Auswertung". 2 Tests (Check-Hinweis, Datenschutz-Abschnitt). 74/74 grün. npm run build nötig.
2026-06-19 Phase 3 · Dual-KI OpenAI als zweiter Anbieter, umschaltbar über KLARHEITS_CHECK_KI (anthropic | openai) → config('klarheits_check.ki.provider'). openaiText() (Chat Completions, response_format: json_object, max_completion_tokens, Http::withToken) neben anthropicText(); gemeinsamer Prompt/Vertrag/Fallback. .env.example dokumentiert. +1 Test (Switch ruft OpenAI, nicht Anthropic). 72/72 grün.