Zum Hauptinhalt springen

In-App-Assistent (OP-AI-4) — Design

Status: in Umsetzung, weitgehend gebaut — Slices 1–15 ✅ (zuletzt 03.07.2026, v0.57.0; Slice-Historie in §9, Folge-Slices dort „Noch offen") · Bezug: OP-AI-4 (Owning-OP, HANDOFF §4) · OP-DOCS-2/-3/-5/-7 · OP-AI-1 · OP-AUTH-1 · OP-AUDIT-1 · OP-OBS-1 · Zielgruppe: IT-Dev/Architektur · True North: direkt — die Werkzeuge auslastung · fahrzeugStandort · freieKapazitaet (§6) beantworten alle drei Leitfragen im Dialog.

Kernaussage. Konversationeller Assistent in der App als drittes Frontend über demselben Wissens-/State-Kern: erweitert den reinen Doku-Chatbot (OP-DOCS-2) um Feedback-Erfassung und Insights über Daten, Planung und die App selbst — Leitentscheidung: vorschlagend, nicht ausführend (§3). Dieses Dokument entstand als Design-Stand vor der Umsetzung („Optionen zuerst, dann bauen", docs/konventionen/agents.md §5.1) und ist inzwischen weitgehend gebaut. Getroffene Entscheidungen (Nutzer 07-01): EU-gehostetes LLM (DSGVO-Residenz), RAG-Retrieval (Vectorize, mit Keyword-Fallback), Feedback→GitHub-Issue, App-first (Doku-Widget als Folge-PR umgesetzt).

1. Zweck & True-North-Bezug

Der Assistent muss die Top-3-Leitfragen (CLAUDE.md / docs/Sanity-Checkliste.md) direkter, schneller oder genauer beantworten — sonst gehört eine Fähigkeit nicht hinein:

  1. Wie hoch ist die Auslastung?
  2. Wo stehen die Fahrzeuge? (physisch und in der Bearbeitung)
  3. Wann sind die nächsten freien Kapazitäten?

2. Drei Säulen

#SäuleWasQuelleBezug
1Doku-Q&A„Wie funktioniert X?"publizierte/Repo-Doku (Retrieval)OP-DOCS-2 / -3 (gemeinsamer Index)
2FeedbackBug/Wunsch/Verbesserung im Dialog meldenNutzereingabe → git-nah abgelegtOP-DOCS-7 (Issue/Discussion-Schleife)
3InsightsAuslastung · Standort · freie Kapazität · „warum sieht der Plan so aus?"Live-DO-State (abgeleitet)OP-AI-1 (Narrative), Konflikt-Engine

3. Leitentscheidung: vorschlagend, nicht ausführend

Der Assistent schlägt vor — der Nutzer wendet an. (Entscheidung 06-22, OP-AI-4.)

  • Lesen/Insights: uneingeschränkt im Rahmen der Berechtigung (siehe §5).
  • Hebel: Der Assistent darf Konflikt-Hebel vorschlagen und ihre Konsequenz simulieren (über die bestehende, server-seitige Engine konfliktHebel / loeseKonflikt — keine Doppellogik), z. B. „Wunsch-Abholtermin anpassen", „optionalen Schritt überspringen", „als Eilauftrag priorisieren".
  • Anwenden: immer mit expliziter Nutzer-Bestätigung — derselbe „Anwenden/Verwerfen"-Pfad wie die Optimierungs-Vorschau (R8-G2). Kein Auto-Apply.

4. Architektur

Bestehender Stack (CLAUDE.md → Architektur): Angular-Client · Cloudflare Worker + Durable Object (partyserver, Live-State als DO-Blob, WebSocket-Broadcast) · LLM-API (A-5-Pragma).

Warum DO-seitig: Der Live-State liegt im Durable Object; Insights werden dort abgeleitet (nie persistiert, G-/S-„Derived State"). Der Assistent-Endpoint sitzt damit am selben Ort wie State + Engine — kein zweiter Datenpfad, keine Drift.

5. Datenzugriff & Berechtigung (hart)

  • Identität: Cloudflare Access (Zero Trust, OP-AUTH-1). Der Assistent sieht nur, was der angemeldete Nutzer sehen darf.
  • Doku-Sichtbarkeit: intern vs. extern muss respektiert werden (OP-DOCS-5) — der Retrieval-Kern liefert nur berechtigte Dokumente; „nicht reverse-engineerbar" (OP-AUDIT-1).
  • Insights: nur aus dem für den Nutzer sichtbaren DO-State; nichts speichern, immer ableiten.
  • Aktionen: Hebel-Vorschläge laufen durch dieselbe Server-Validierung wie die manuelle Bedienung — der Assistent kann nichts, was der Nutzer nicht auch von Hand dürfte.

6. Werkzeuge (Tool-/Function-Calling)

Statt rohem Kontext bekommt das LLM definierte Tools auf den Live-State/Engine (read-only bzw. simulierend); Anwenden bleibt ein separater, vom Nutzer bestätigter Schritt.

ToolArtLiefert
dokuSuche(frage)readrelevante Doku-Passagen (Retrieval, berechtigungsgefiltert)
auslastung(zeitraum)readAuslastung MA/Buchten (True-North 1)
fahrzeugStandort(auftrag?)readphysischer Slot + Bearbeitungsphase (True-North 2)
freieKapazitaet(leistung?, ab?)readnächste freie Zeitfenster (True-North 3, vgl. Angebotsmodus §5.2.2)
planNarrativ(auftrag?)read„warum sieht der Plan so aus / was hat der Optimierer geändert" (OP-AI-1)
hebelVorschlagen(konflikt)simulateHebel + Konsequenz-Diff (konfliktHebel) — kein Apply
feedbackAnlegen(text)write (git-nah)strukturiertes Issue/Discussion (OP-DOCS-7)
issueSuche(frage)readoffene GitHub-Issues zu einem Thema (Duplikate vermeiden)
datenListe(was, filter?)readkonkrete Live-Listen (Mitarbeiter/Aufträge/Skills/Leistungen/Buchten)
datenmodell()readverbindliche Wertebereiche/Enums der Domäne (Skalen, Status-Lebenszyklen) — Anti-Halluzination

Grounding gegen Halluzination: Der Datenmodell-Steckbrief (datenmodell.ts, DATENMODELL) steht immer im System-Prompt (Skalen 1–3, Status-Lebenszyklen, Phasen). Zwei feste Regeln: der Assistent nennt konkrete Mitarbeiter/Namen/Aufträge/Zahlen nur aus echten Werkzeug-Ergebnissen (sonst ehrlich „keine Daten") und hält sich strikt an die Wertebereiche (kein erfundenes Niveau 4/5). Auslöser: der Assistent erfand ein „Niveau 4" bzw. Mitarbeiter/Fakten.

Werkzeugsatz je Frontend (OP-DOCS-2): die App bekommt alle Werkzeuge; ein freigegebenes Cross-Origin- Doku-Frontend (docs.taktano.com, per ASSISTENT_CORS_ORIGINS) bekommt server-seitig nur dokuSuche + datenmodell (werkzeugFilter in fuehreDialog) — keine Live-/Betriebsdaten übers Doku-Widget (OP-DOCS-5, OP-FEEDBACK-2). Feedback läuft für beide über den deterministischen /feedback-Endpoint.

7. UI-Einstieg

  • Ein Assistent-Panel (z. B. dock-/sheet-artig), aus jedem Workspace erreichbar; Modi (Doku / Insights / Feedback) ergeben sich aus der Frage — kein Moduswechsel-Zwang (Abgrenzung „ein Frontend mit Modi" statt zwei separaten Tools).
  • Selbsterklärbarkeit (docs/konventionen/agents.md §5.1): Werkstatt-Sprache, sprechende Aktionen.
  • Design-System: Tokens aus design/, keine Hardcodes; Patterns aus design/COMPONENTS.md. Hebel-Apply nutzt den vorhandenen „Anwenden/Verwerfen"-Pfad (R8-G2) — kein neuer Bestätigungs-Flow.
  • Observability: OTel je Laufzeit; LLM-Calls/Tool-Calls über den Logger-Wrapper, trace_id-korreliert (OP-OBS-1).

8. Entscheidungen (07-01, Nutzer) — vormals offene Punkte

  • Retrieval-Kern: RAG/Embedding-Index (Cloudflare Vectorize DOKU_INDEX, EU-Embeddings) als Produktionspfad, mit Keyword-Fallback über den gebündelten doku-index.generated.ts (kein externer Dienst nötig, deploy-sicher). Reiner Kern in server/src/assistant/retrieval.ts (ein Kern, mehrere Frontends — OP-DOCS-2/-3); Re-Index beim Doku-Deploy (OP-DOCS-1 d) — Aktivierung siehe wrangler.toml.
  • LLM-Provider: EU-gehostetes, OpenAI-kompatibles Modell (DSGVO-Residenz, z. B. Mistral „La Plateforme"). Dormant ohne ASSISTENT_LLM_KEY (Endpoint 503, deploy-sicher). Compliance-Beleg: docs/betrieb/Compliance.md; Risiko/Datenfluss: docs/betrieb/Risikoregister.md.
  • Feedback-Ablage: GitHub-Issue (labelbar, git-nah, OP-DOCS-7), reiner Mapper server/src/assistant/feedback.ts, dormant ohne TAKTANO_FEEDBACK_TOKEN. Kein Klartext-PII im Issue-Body (Actor nur im Audit-Log, OP-AUDIT-1/OP-LOG-1).
  • Kosten/Rate-Limits/Fallback: Rundenlimit je Turn (MAX_RUNDEN), Feature-Flag-Allowlist (ASSISTENT_ALLOWLIST), graceful „nicht erreichbar"-Fallback (analog Solver-Fallback).
  • Self-Test/CI: drei self-asserting Demos verdrahtet — test:assistant-retrieval · -tools · -feedback (.github/workflows/ci.yml); Client↔Server-DTO-Symmetrie über assistent.types.ts.

8.1 Nachtrag (07-02, Nutzer) — Vectorize/RAG: Pain/Gain-Analyse + Aktivierung

Kernaussage: RAG ist aktiviert (Nutzer-Entscheidung 07-02 „Implement RAG" — überstimmt die am selben Tag zunächst beschlossene kriteriengebundene Zurückstellung, s. u.). Die Aktivierung ist Everything-as-Code: scripts/push-doku-index.mjs (npm run doku-index:push) provisioniert den Index selbst (taktano-doku, 1024 Dims/cosine = mistral-embed), embeddet die frisch gechunkte Doku und upsertet sie; der CI-deploy-Job führt ihn vor wrangler deploy aus (Binding existiert garantiert), der docs-deploy-Job re-indext bei Doku-only-Änderungen (OP-DOCS-1 d). Ohne Secrets bleibt alles dormant-sicher (Keyword-Fallback). Bekannte, bewusste Grenze: Upsert überschreibt nur aktuelle Chunk-IDs — nach großen Doku-Umbauten können veraltete Vektoren zurückbleiben (Index einmalig neu anlegen + pushen). Die zugrunde liegende Pain/Gain-Analyse (07-02):

  • Gain (semantisch vs. Keyword): Recall bei Vokabular-Mismatch — deutsche Komposita (Folienzuschnitt vs. „Folie zuschneiden"), Flexion (Buchten/Bucht), Nutzer- vs. Glossar-Sprache („Wagen" vs. Fahrzeug, „Reserviert" vs. „Fahrzeug angeliefert"), Tippfehler. Für interne Nutzer (kennen die Begriffe) ist der TF-IDF-Fallback über ~460 Chunks heute ausreichend; kritisch wird der Unterschied bei externen Widget-Nutzern und mit wachsendem Korpus.
  • Pain: zwei zusätzliche Query-Zeit-Abhängigkeiten (Embedding-API + Vectorize, ~100–300 ms; Fallback fängt beide) · Index-Frische-Pipeline (doku-index:push je Doku-Deploy, Drift-Risiko wenn vergessen — der Keyword-Index ist dagegen immer exakt so frisch wie der Deploy) · Kosten vernachlässigbar (~460 × 1024 Dims, mistral-embed im Cent-Bereich).
  • ⚠ Residenz-Flag (DE/AT/CH, → RISK-17 / C-19): Vectorize bietet keine jurisdiction = "eu"-Option (anders als R2) — Index-Daten liegen global. Gespeichert sind nur Doku-Chunks + Metadaten (inkl. sichtbarkeit:'intern'), kein PII, keine Kunden-/Tenant-Daten → DSGVO-Exposure gering; die Frage-Embeddings laufen über das EU-Modell (C-17), nur der Vektor geht an Vectorize. Die EU-Residenz-Entscheidung (07-01) deckt das Embedding-/LLM-Modell ab, nicht den Vektor-Store — daher Compliance-Zeile C-19 + Risiko RISK-17 (akzeptiert; periodisch prüfen, ob CF EU-Jurisdiktion für Vectorize nachliefert).

Historie: Die Analyse empfahl zunächst eine kriteriengebundene Zurückstellung (aktivieren erst bei externer Doku-Sicht/Widget ODER realen Recall-Beschwerden) — vom Nutzer am selben Tag (07-02) mit „Implement RAG" überstimmt; da Code + Fallback fertig waren, beschränkte sich die Umsetzung auf die Aktivierungsschicht (Push-Script · Binding · CI-Wiring · Compliance-Nachzug C-19/RISK-17).

Noch offen (Folge-PR): Doku-Widget auf docs.taktano.com (Cross-Origin/Access-Cookie-Scope, OP-DOCS-2 §6-Frontend); externe vs. interne Doku-Sicht scharf schalten (OP-DOCS-5).

9. Stand der Umsetzung

Slice 1 — Insights-Engine (read-only), umgesetzt (22.06.2026):

  • server/src/insights/insights.ts — reine, server-unabhängige Funktionen für die Top-3-Leitfragen: auslastung (belegte Buchten/Quote · Aufträge in Arbeit · beschäftigte Mitarbeiter), fahrzeugStandorte (physischer Slot + bearbeitungsphase), freieKapazitaet (delegiert an den Angebotsmodus scheduler/angebot, keine Doppellogik). Alle Antworten abgeleitet aus einer schmalen Snapshot-Projektion (InsightInput), nichts gespeichert (D-2). Sprechende deutsche Klartext-Ausgaben.
  • server/src/insights/insights-demo.ts — self-asserting Demo (npm run test:insights), in CI verdrahtet (.github/workflows/ci.yml, Server-Job). Verifiziert Auslastung, Standort/Phase und freie Kapazität gegen den echten Seed.

Slice 2 — DO-Anbindung (read-only), umgesetzt (22.06.2026):

  • server/party/leitstand.tssnapshot() projiziert die bereits berechneten DTOs (auftraege, belegung) auf die schmale InsightInput-Form und berechnet auslastung + fahrzeugStandorte live bei jedem Broadcast; Ergebnis als neues, additives Feld insights in LeitstandState. Nichts gespeichert (D-2). Rückwärtskompatibel: der Angular-Client castet eingehendes JSON und ignoriert unbekannte Felder — kein Client-Bruch. freieKapazitaet (parametrisiert) bleibt der on-demand-/ LLM-Schicht vorbehalten. Verifiziert: npm run typecheck grün.

Slice 3 — Angular-Panel (read-only), umgesetzt (22.06.2026):

  • client/src/app/services/leitstand.service.tsAuslastungInsight/StandortInsight/InsightsDTO zum Server-Vertrag gespiegelt, additives Feld insights in LeitstandState + EMPTY_STATE + Selektor insights.
  • client/src/app/leitstand/insights-panel.component.ts — Karte „Lage auf einen Blick": Frage 1 (Buchten-Quote mit Balken · Aufträge in Arbeit · beschäftigte Mitarbeiter) + Frage 2 (Standort-Liste physisch + sprechende Bearbeitungsphase, farbcodiert über Status-Tokens; rot bleibt Warnungen vorbehalten). Rein darstellend, Tokens statt Hex (CI-Gate check-no-hex), Patterns aus COMPONENTS.md.
  • Eingebunden in pages/overview.component.ts (Überblick-Raum) unter dem Produktionsfluss-Band. Verifiziert: npx ng build --configuration production grün; Komponenten-Vorschau mit repräsentativen Daten gerendert (kompilierte App-CSS + Tokens).

Slice 4 — LLM-Tool-Calling-Endpoint (Säule 1 + Insights), umgesetzt (01.07.2026):

  • server/src/assistant/{types,llm,tools,handler,retrieval,feedback}.ts — reine, testbare Kerne: EU-LLM-Client (OpenAI-kompatibel, LlmClient-Interface → Fake-testbar), Tool-Registry (7 Werkzeuge: dokuSuche, auslastung, fahrzeugStandort, freieKapazitaet, planNarrativ, hebelVorschlagen [nur Simulation, §3], feedbackAnlegen), Dialog-Orchestrierung (System-Prompt → Tool-Schleife → Antwort, Rundenlimit) und Keyword-/Vectorize-Retrieval. Doku-Index aus docs/**/*.md (scripts/build-doku-index.mjsdoku-index.generated.ts, npm run doku-index).
  • server/party/leitstand.tsonRequest-Endpoint /assistant am DO (Live-State + Engine am selben Ort, §4): bindet den ToolKontext an insights.ts, konfliktHebel (simulierend) und die Doku-Suche; dormant ohne LLM-Key (503). Verifiziert: npm run typecheck grün; test:assistant-retrieval|-tools|-feedback grün (in CI).

Slice 5 — Feedback→GitHub-Issue (Säule 2), umgesetzt (01.07.2026):

  • server/src/assistant/feedback.ts — reiner Mapper Feedback→Issue (Titel/Body/Labels, Element-Verortung, kein Klartext-PII). DO-Methode feedbackAnlegen/erstelleFeedbackIssue (dormant ohne TAKTANO_FEEDBACK_TOKEN, Muster wie pullGithubKosten) + Audit-Event assistent.feedback (OP-AUDIT-1).

Aktivierung (07-01): Die beiden Secrets ASSISTENT_LLM_KEY (Mistral EU) und TAKTANO_FEEDBACK_TOKEN werden im CI-deploy-Job (.github/workflows/ci.yml) aus gleichnamigen GitHub-Secrets vor wrangler deploy nach Cloudflare gespiegelt (dormant-sicher: nicht gesetzt → übersprungen). Der Feedback-Token trägt bewusst kein GITHUB_-Präfix (in GitHub-Actions-Secrets reserviert/verboten). Voraussetzung: der Worker taktano existiert bereits (Sync vor Deploy ist dann unkritisch).

Slice 6 — Angular-Assistent-Panel + Element-Feedback (Säule 1–3), umgesetzt (01.07.2026):

  • client/src/app/services/chat.service.ts — signals-basiert (wie ToastService), POSTet den DO-Endpoint (same-origin ⇒ Access-Cookie), injiziert Route/Workspace-Kontext.
  • client/src/app/leitstand/chat-panel.component.ts — ein Assistent-Panel (Drawer rechts) aus jedem Workspace erreichbar (§7), Tokens statt Hex (CI-Gate check-no-hex), tk-popin/tk-ease.
  • client/src/app/services/feedback-pin.service.ts + feedback-pin-overlay.component.ts — „Feedback zu diesem Element": Capture-Klick löst das Element in einen stabilen Anker (data-tk-id/CSS-Pfad) auf. Eingebunden in shell.component.ts neben Toasts/Repark-Modal.

Slice 7 — Deterministischer Feedback-Pfad (Fix „kein Issue"), umgesetzt (01.07.2026):

Der bisherige Pfad hing davon ab, dass das LLM das Tool feedbackAnlegen aufruft, und maskierte API-Fehler als „Feedback vermerkt" — beides konnte dazu führen, dass kein Issue entstand.

  • Server (leitstand.ts): neuer DO-Endpoint /feedback (in onRequest, kein LLM); die Issue- Erstellung ist in erstelleFeedbackIssue(): FeedbackErgebnis extrahiert und liefert ehrlich { ok, issueUrl?, status?, grund? } (dormant/HTTP-Status/Ausnahme). Der LLM-Tool-Wrapper nutzt denselben Kern und gibt jetzt echte Fehlergründe zurück. Reiner Grund-Mapper feedbackFehlerGrund in feedback.ts (test:assistant-feedback erweitert). Audit-Event assistent.feedback bleibt bei jedem Versuch.
  • Client: feedback.service.ts POSTet direkt /feedback und quittiert ehrlich — Erfolgs-Toast mit Issue-Bezug bzw. Fehler-Toast mit echtem Grund (z. B. „HTTP 403 — Token-Scope"). Neues feedback-form.component.ts (Art-Chips + Text), der Pin öffnet es statt des Chats. Damit ist das Anlegen deterministisch und Konfig-Fehler sind selbst-diagnostizierend. Verifiziert: typecheck + test:assistant-feedback + ng build + No-Hex grün.

Slice 8 — Duplikat-Erkennung · Assistent liest Issues + Live-Listen · Erfolgs-Quittung (01.07.2026):

  • Read-only Issue-Suche (server/src/assistant/github-issues.ts, pure): sucheTerme + githubIssueSuchPfad (GitHub-Search-API, offene Issues) + issuesAusSuche. DO-Methode sucheOffeneIssues (dormant ohne TAKTANO_FEEDBACK_TOKEN). Geteilt von (a) Duplikat-Prüfung und (b) neuem Assistent-Tool.
  • Duplikat-Erkennung (/feedback/aehnliche): vor dem Anlegen sucht der Server **ähnliche offene Issues
    • OPs** (OPs via keywordSuche über den OP-Katalog: Lastenheft §11 / Register / IDs). Client zeigt sie als „Ähnliche offene Einträge"; der Nutzer bestätigt mit „Trotzdem anlegen" (Entscheidung 07-01: warn + confirm, nicht-blockierend).
  • Assistent-Tools (tools.ts): issueSuche (offene Issues nachschlagen / vor Feedback Duplikate vermeiden) und datenListe (konkrete Listen aus dem Live-State: Mitarbeiter/Aufträge/Skills/ Leistungen/Buchten, optional gefiltert, große Listen gekappt). System-Prompt: Daten-/Listen-Fragen mit echten Daten beantworten (datenListe), Bedienungshinweise nur bei zu großer Datenmenge oder ausdrücklicher Bedienungsfrage (Nutzer-Wunsch 07-01 — behebt „ich habe keinen Zugriff auf Live-Daten").
  • Erfolgs-Quittung: nach dem Anlegen Toast „Feedback angelegt · Issue #N" (Nummer aus issueUrl).
  • Sichtbarkeit: die Issue-/Daten-Zugriffe sind intern gedacht → Kunden-Einschränkung als OP-FEEDBACK-2 (HANDOFF §4) geparkt. Verifiziert: typecheck + test:assistant-tools|-feedback + ng build + No-Hex grün.

Slice 9 — Datenmodell-Grounding + Anti-Halluzinations-Regel (02.07.2026):

  • Datenmodell-Steckbrief server/src/assistant/datenmodell.ts (DATENMODELL: EnumFakt[]): die verbindlichen Wertebereiche/Enums der Domäne (Skill-Niveau/Vertrauensstufe/Güteklasse/Anspruch 1–3, Auftrag-Status-Lifecycle, Teilschritt-/Mitarbeiter-Status, fachliche Phasen, Abhängigkeit, Schicht-/Leistung-Status) — synchron zu model/types.ts + operativ/auftrag.ts (OP-DOCS-9). datenmodellPrompt() (kompakt, immer im System-Prompt) + datenmodellText() (Tool).
  • Anti-Halluzination (handler.ts): der Steckbrief steht immer präsent im System-Prompt; zwei feste Regeln — „ERFINDE NIEMALS Fakten" (Mitarbeiter/Namen/Aufträge/Zahlen/Status nur aus echten Werkzeug-Daten, sonst ehrlich „keine Daten") und „halte dich strikt an die Wertebereiche" (kein Niveau 4/5, Skala 1–3; die frühere Einzeiler-Niveau-Regel aus Slice-Nachtrag #282 ist darin aufgegangen).
  • Neues Tool datenmodell (tools.ts, Registry 9 → 10, gebunden in leitstand.ts) für Schema-/Wertebereichs-Fragen. Auslöser (Nutzer): der Assistent erfand ein „Niveau 4" bzw. Mitarbeiter/Fakten. Verifiziert: typecheck + test:assistant-tools (10 Werkzeuge · Dispatch datenmodell→„1–3" · System-Prompt enthält „ERFINDE NIEMALS") grün.

Slice 10 — Screenshot-Feedback mit Bild-Schutz + nummerierten Markierungen (02.07.2026, OP-FEEDBACK-1):

  • Aufnahme (client/src/app/services/screenshot.service.ts + leitstand/screenshot-region-overlay.component.ts, in shell.component.ts gemountet): „📷 Screenshot aufnehmen" öffnet ein Ziehen-zum-Auswählen-Overlay; auf Loslassen wird html2canvas-pro lazy geladen (eigener Chunk; seit 02.07.2026 der gepflegte ESM-Fork statt html2canvas — versteht moderne CSS-Farbfunktionen wie color()/oklch, an denen iPadOS/Safari die Aufnahme scheitern ließ), die Seite gerendert und auf die Region zugeschnitten → image/png-Blob. Overlay + Formular tragen data-tk-screenshot-ignore → nicht im Bild.
  • Immer Bild-Schutz + Markierungen: das Feedback-Formular bettet app-bild-upload-gate [bildQuelle] mit dem aufgenommenen Bild ein (Gate aus PR-A/Bildbewertung: Qualität · PII-Verpixeln · NSFW · nummerierte Markierungen); auf freigegeben {blob, markierungen} lädt der Client das geschützte + annotierte Bild hoch.
  • Transport: neuer Outer-Fetch-Zweig /api/feedback/screenshot (leitstand.ts): POST → FOTOS-Key feedback/<uuid>{ r2Key }; dormant → 503 ohne FOTOS (Feedback filet weiter als Text).
  • Ans Issue: FeedbackAnfrage (+ Client-Spiegel) trägt screenshotKey? + markierungen?; der /feedback- Handler baut den Access-gated Link aus url.origin, feedbackZuIssue hängt eine „Screenshot (intern, Access-gated)"-Sektion + die Markierungen an den Body. Intern gedacht → Kunden-Sichtbarkeit = OP-FEEDBACK-2.
  • Verifiziert: test:assistant-feedback (Link + Markierungs-Mapping) · test:bildgate · typecheck · ng build · No-Hex grün. ➡ OP-FEEDBACK-1 vollständig (PR-A Markierungs-Layer + PR-B Screenshot-Fluss).

Slice 11 — Doku-Widget auf docs.taktano.com (02.07.2026, OP-DOCS-2 §6-Frontend): „ein Kern, mehrere Frontends" — dasselbe Assistent-/Feedback-Backend, jetzt als schwebendes Widget auf der (Access-gegateten, internen) Doku-Seite. Entscheidung (Nutzer): Doku-Seite intern-only; Widget-Umfang Doku-Q&A + Feedback (keine Live-/Betriebsdaten über das Doku-Frontend).

  • CORS (App-Worker): reiner Helfer server/src/http/cors.ts (parseCorsAllowlist/corsErlaubt/corsHeaders — spiegelt den konkreten Origin, nie * mit credentials; test:cors). Im Worker-fetch: OPTIONS- Preflight vor dem Access-Check beantwortet (Preflight trägt kein Cookie), CORS-Header via mitCors auf die /parties/…- (Assistent/Feedback) und /api/feedback/screenshot-Antworten gelegt. Neue Env ASSISTENT_CORS_ORIGINS (kommagetrennt; leer ⇒ kein Cross-Origin, Same-Origin-App unberührt).
  • Doku-Frontend = beschränkter Werkzeugsatz: derselbe Origin-Allowlist-Treffer, der CORS gewährt, markiert im DO das Doku-Frontend → fuehreDialog bekommt werkzeugFilter: ['dokuSuche','datenmodell'] (kein datenListe/issueSuche/auslastung/… — keine Betriebsdaten übers Doku-Widget, OP-DOCS-5/OP-FEEDBACK-2) + einen System-Zusatz. intern bleibt true (Seite ist staff-only → volle Doku-Sicht korrekt).
  • Widget (Docs-Site): SSR-sicheres Vanilla-Client-Modul docs-site/src/clientModules/assistentWidget.js (schwebender Button → Panel mit Tabs Doku-Frage/assistant und Feedback/feedback, credentials:'include'), App-Origin über <meta name="taktano-app-origin"> (headTags/customFields.appOrigin), Styling über Infima-Variablen (custom.css, kein --tk-*). Dormant-/fehlersicher (503/Netz → Hinweis).
  • ⚠ Voraussetzung produktiv (Infra, nicht Code) — Annahme hielt nicht (07-03, → Slice 12): docs.taktano.com und app.taktano.com sind getrennte Origins, beide einzeln Edge-Access-geschützt. Die Annahme „Access-Vars leer (JWT-Verify aus, OP-COST-3) → der Fetch klappt mit CORS" verwechselte die Worker-interne JWT-Prüfung mit dem Edge-Gate: Cloudflare Access vor app.taktano.com fing den CORS-Preflight (OPTIONS trägt nie ein Cookie) und den credentials-POST (kein App-Access-Cookie im Docs-Kontext) mit einem Login-Redirect ohne CORS-Header ab → fetch warf → Widget zeigte pauschal „Server nicht erreichbar" (Nutzer-Befund 07-03). Fix: Same-Origin-Proxy, Slice 12.
  • Verifiziert: test:cors · test:assistant-tools (Doku-Modus → nur dokuSuche+datenmodell erreichen das LLM) · typecheck · docs-site npm run build (Modul SSR-sicher). ➡ OP-DOCS-2 §6-Frontend gebaut.
  • RAG aktiviert (07-02, v0.51.0): scripts/push-doku-index.mjs (npm run doku-index:push) = Index- Self-Provisioning (taktano-doku, 1024/cosine) + Batch-Embedding (mistral-embed) + NDJSON-Upsert (Metadaten spiegeln den rankVectorize-Vertrag; Chunk-Quelle = JSON-Sidecar aus npm run doku-index, Build-Artefakt); [[vectorize]]-Binding DOKU_INDEX in wrangler.toml aktiv; CI: deploy-Job pusht VOR wrangler deploy, docs-deploy re-indext bei Doku-only-Änderungen (OP-DOCS-1 d). Dormant-sicher ohne Secrets; nicht-blockierend (Review #304: Timeout + Retry + Warn-Exit-0 im Script, continue-on-error im Workflow — Deploy-Verfügbarkeit > Index-Frische); Laufzeit-Ausfall → Keyword-Fallback. Compliance/Risiko: C-19 · RISK-17 (§8.1). ➡ OP-DOCS-2-Retrieval vollständig (semantisch live).

Slice 12 — Doku-Widget-Fix: Same-Origin-Proxy statt Cross-Origin-Fetch (03.07.2026, Bugfix v0.53.1):

Kernaussage: Das Doku-Widget funktionierte produktiv nicht — jede Frage endete mit „⚠ Server nicht erreichbar" (Nutzer-Screenshot 07-03). Ursache: der Cross-Origin-Fetch docs.taktano.com → app.taktano.com kam nie am Worker an, weil Cloudflare Access an der Edge vor app.taktano.com Preflight und POST zum Login umleitete (Redirect ohne CORS-Header ⇒ fetch-TypeError ⇒ Catch-Zweig des Widgets). Die Worker-seitige CORS-/OPTIONS-Behandlung (Slice 11) war korrekt, lief aber hinter dem Edge-Gate und wurde nie erreicht.

  • Fix (Everything-as-Code, statt Access-Dashboard-Konfiguration): Same-Origin-Proxy auf der Docs-Site — Pages-Function docs-site/functions/parties/[[pfad]].js + Service-Binding APP_WORKER auf den Worker taktano (docs-site/wrangler.toml). Das Widget POSTet jetzt relativ (/parties/… auf docs.taktano.com, Default-Origin leer in docusaurus.config.js/Widget); die Function reicht Methode/Header/Body per Binding weiter — Worker-zu-Worker, nicht über die Edge ⇒ kein CORS-Preflight, kein zweites Access-Cookie nötig, keine Access-App-/Cookie-Sonderkonfiguration (Alternative „gemeinsame Access-App + Access-CORS-Settings" wäre Dashboard-Zustand außerhalb des Repos und hätte zusätzlich einen bestehenden app.taktano.com-Login im Browser vorausgesetzt).
  • Sicherheit unverändert: Access gated weiterhin docs.taktano.com (Seite intern-only) — erst dahinter ist der Proxy erreichbar. Der Proxy ist bewusst eng (nur POST, nur assistant · feedback · feedback/aehnliche; kein WS-/State-Durchgriff). Der Browser setzt bei POST den Origin-Header auch same-origin → der DO erkennt das Doku-Frontend wie bisher über ASSISTENT_CORS_ORIGINS (werkzeugFilter: dokuSuche + datenmodell). Die CORS-Schicht (Slice 11) bleibt für echte Cross-Origin-Frontends (Preview/Staging via TAKTANO_APP_ORIGIN-Override) bestehen.
  • ⚠ Folge-Hinweis (OP-COST-3): Wird die Worker-interne JWT-Prüfung (ACCESS_TEAM_DOMAIN/ACCESS_AUD) scharf geschaltet, trifft sie auch die per Binding weitergereichten Widget-Requests — das mitgesendete CF_Authorization-Cookie stammt dann von der Docs-Access-App (anderes AUD). Dann entweder gemeinsame Access-App über *.taktano.com (ein AUD) oder das Docs-AUD zusätzlich akzeptieren.
  • Verifiziert: docs-site npm run build grün (Widget/Config SSR-sicher) · Proxy-Pfad-Allowlist per Node-Snippet geprüft · typecheck Server grün (nur Versions-Bump).

Slice 13 — Feedback-Übersicht: wo gibt es offenes/geschlossenes Feedback? + Issue-Liste in der Doku (03.07.2026, v0.54.0, Nutzer-Wunsch):

Kernaussage: App und Doku zeigen jetzt read-only, wo es Feedback gibt und in welchem Status — gespeist aus den GitHub-Issues, die der Feedback-Pfad ohnehin anlegt (Label feedback, OP-DOCS-7); kein zweiter Datenbestand.

  • Server: neue reine Helfer in github-issues.tsgithubFeedbackListePfad (List-Issues-API, labels=feedback&state=all, jüngste Aktivität zuerst), issuesAusListe (PRs raus, Status sprechend offen/geschlossen) und routeAusIssueBody (liest die vom Anlegen strukturiert hinterlegte Verortung/Route aus dem Issue-Body zurück → „wo?" ohne zweites Datenfeld). Neuer read-only DO-Endpoint GET /feedback/liste (FeedbackListe; dormant ohne TAKTANO_FEEDBACK_TOKEN, ehrlicher Grund; Berechtigung wie /assistant). DTO-Symmetrie: FeedbackIssue/FeedbackListe in Server- und Client-assistent.types.ts.
  • App: das Feedback-Formular lädt beim Öffnen die Übersicht „Bisheriges Feedback" — je Issue Status-Badge (● offen amber / ✓ geschlossen grün), Titel-Link, Route (monospaced). Beantwortet vor dem Melden zugleich „gibt es hier schon etwas?" (ergänzt die Duplikat-Prüfung).
  • Doku-Widget: dritter Tab „Issues" (read-only) — dieselbe Liste über den Same-Origin-Proxy (Slice 12; Allowlist um GET feedback/liste erweitert). Formular ist im Issues-Tab ausgeblendet (.tk-assi-form[hidden]-CSS, gleiche Falle wie einst bei den Art-Chips).
  • Verifiziert: test:assistant-feedback (Listen-Pfad · Route-Rücklese · Status-/PR-Mapping) · Server-tsc · ng build + No-Hex · docs-site build · Proxy-Allowlist-Check (POST+GET) grün.

Slice 14 — Feedback-UX-Umbau: Ein-Schritt-Screenshot + Feedback-Overlay statt Dialog-Liste + globaler Toggle (03.07.2026, v0.55.0, Nutzer-Feedback „der Feedback-Dialog ist nicht gut"):

Kernaussage: Drei Nutzer-Wünsche umgesetzt — (a) Element auswählen und Screenshot inkl. Nachbearbeitung sind EIN Schritt, (b) bisheriges Feedback erscheint als Overlay/Tooltip direkt in App und Doku (nicht mehr als Liste im Dialog), (c) globaler Ein-/Aus-Toggle analog zum Light/Dark-Umschalter.

  • Ein Schritt (App): neues Vollbild-Overlay screenshot-editor-overlay.component.ts — öffnet direkt nach der Aufnahme (Region-Ziehen ODER Element-Auswahl) die Nachbearbeitung (Verpixeln + nummerierte Markierungen, bestehendes app-bild-upload-gate); „Hochladen" hängt das geschützte Bild an, das Formular zeigt nur noch „✓ Screenshot angehängt". Der Feedback-Pin erfasst das angeklickte Element automatisch als Screenshot (sichtbarer Element-Ausschnitt + 12 px Rand, best-effort — scheitert die Aufnahme, bleibt Text-Feedback möglich). Formular/Pin-Overlay tragen data-tk-screenshot-ignore und landen nie im Bild.
  • Feedback-Overlay (App): feedback-marker-overlay.component.ts — Marker am verorteten Element (Selektor wird per neuem selektorAusIssueBody aus dem Issue-Body zurückgelesen → FeedbackIssue.selektor, kein zweiter Datenbestand) mit Klick-Tooltip (Status ● offen amber / ✓ geschlossen grün · Titel-Link); Route-Feedback ohne (auffindbaren) Anker sammelt ein Seiten-Chip unten links. Positionen folgen Scroll/Resize/Navigation (rAF-gedrosselt). Die frühere Liste „Bisheriges Feedback" im Dialog ist entfernt.
  • Feedback-Overlay (Doku): Navbar-Toggle 💬 (neben Light/Dark, type: 'html'-Navbar-Item, Delegation im Client-Modul) + Seiten-Chip über dem Assistent-Button mit Popover der Feedback-Issues dieser Seite (Route = Pathname). Widget-Tab „Issues" (Gesamtliste) bleibt.
  • Toggle: App-Rail-Button „Feedback-Anzeige" (unter dem Theme-Umschalter, gleiche Bedien-Sprache) bzw. Doku-Navbar 💬; Zustand persistiert in localStorage (tk-feedback-overlay), Default aus (kein Zwangs-Overlay), beim Einschalten lädt die Übersicht.
  • Verifiziert: test:assistant-feedback (Selektor-Rücklese) · Server-tsc · ng build + No-Hex · test:bildgate · docs-site build grün.

Slice 14a — Nachtrag: neues Feedback in Echtzeit an alle (03.07.2026, v0.56.0, Nutzer-Wunsch): Neues Feedback erschien erst nach Neuladen. Jetzt broadcastet der DO nach erfolgreichem Anlegen (erstelleFeedbackIssue — deckt deterministischen Pfad UND LLM-Tool) ein feedback.neu-Event mit dem fertigen FeedbackIssue (aus den Anlege-Daten gebaut, kein GitHub-Re-Fetch; best-effort) an alle Partykit-Verbindungen. Die App mergt es live in die Übersicht (FeedbackService.issueEingegangen: vorn + dedupliziert → Marker/Chip aktualisieren über die Signals von selbst) und zeigt allen einen dezenten Toast „💬 Neues Feedback · #N". Doku bewusst ohne WebSocket (das Doku-Frontend erhält keine Live-State-Broadcasts — OP-DOCS-5-Schranke): stattdessen 60-s-Frische-Refresh solange das Overlay an ist

  • Refresh bei Seitenwechsel (TTL) + sofort nach eigenem Feedback. Verifiziert: Server-tsc · ng build · docs-site build grün.

Slice 15 — Markdown-Rendering im Doku-Widget + Issue-Details-Tooltip mit Bild-Vorschau + „Issues" im Assistent-Panel (03.07.2026, v0.57.0, Nutzer-Befund + -Wunsch):

Kernaussage: Assistent-Antworten im Doku-Widget werden jetzt als Markdown gerendert (vorher roher ###/**-Text, Nutzer-Screenshot), und die Issue-Übersicht zeigt je Eintrag einen Detail-Tooltip mit Issue-Inhalt und Screenshot-/Bild-Vorschau — in Doku-Widget und App.

  • Markdown-Renderer (mdHtml, Widget-eigen, keine Lib): erst wird ALLES escaped, dann zeilenbasiert umgesetzt — Überschriften #…####, Listen (-/1.), Trennlinien, ```-Code-Blöcke, Inline **fett** / *kursiv* / `Code` / [Links](…) mit Schema-Allowlist (nur http(s)/relativ — javascript: bleibt Text). Kein Roh-HTML aus der Antwort; escapeHtml escapet jetzt auch " (Attribut-sicher). Nutzer-Blasen bleiben Klartext.
  • Server: FeedbackIssue trägt zusätzlich details (Issue-Body ohne <sub>-Meta-Fußzeile und ohne Bild-Markup, auf 600 Zeichen gekappt) + bilder (extrahierte Bild-Links: Markdown-Bilder, <img src>, Links auf Bild-URLs inkl. GitHub-Attachments und dem Access-gated Screenshot-Link aus feedbackZuIssue) — reine Helfer detailsAusIssueBody/bilderAusIssueBody in github-issues.ts, DTO-Symmetrie beidseitig.
  • Doku-Widget: Zeilen mit Details/Bildern zeigen per Hover/Fokus einen Tooltip (.tk-assi-tip, position:fixed → nicht vom Panel-Overflow beschnitten, per JS über/unter der Zeile, bleibt beim Überfahren offen → Bild-Links klickbar); 📷-Marker in der Zeile signalisiert angehängte Bilder. Bilder laden best-effort (Access-/GitHub-gated Quellen brauchen ggf. Anmeldung → Link öffnet im Tab).
  • App (Nutzer-Befund „Issues nur im Chat-Dialog sichtbar — wo in der App?"): neuer „🗂 Issues"-Umschalter in der Kopfzeile des Assistent-Panels (neben „📍 Feedback") zeigt die read-only Gesamtliste direkt im Chat-Drawer — komplementär zum route-bezogenen Feedback-Overlay aus Slice 14 (Marker/Chip = „wo auf DIESER Seite?", Panel-Liste = „alles auf einen Blick"). Wiederverwendbare Komponente feedback-issues-liste.component.ts (Status-Badge · Route · 📷 · Detail-Tooltip mit Bild-Vorschau, verzögertes Ausblenden für klickbare Bild-Links; Details als vorformatierter Klartext — bewusst ohne eigenen Markdown-Renderer im Angular-Client, Einfachheit).
  • Verifiziert: test:assistant-feedback (+10 Asserts: Bild-Extraktion · Details ohne Meta/Bilder · Kappung · Mapping) · Server-tsc · ng build · Widget-JS Syntax-Check grün.

Noch offen (nächste Slices): Frage 3 (freie Kapazität) als Panel-Karte · WS-Streaming/Typing-Indikator · externe (kunden-sichtbare) Doku-Sicht scharf schalten (intern=false + sichtbarkeit-Pflege, OP-DOCS-5) · gemeinsame Access-App über *.taktano.com bzw. Docs-AUD-Akzeptanz, sobald die Worker-JWT-Prüfung scharf geschaltet wird (Infra, s. Slice 12) · Kunden-Sichtbarkeit des Feedbacks einschränken (OP-FEEDBACK-2). Reihenfolge/Details in OP-AI-4 (HANDOFF §4).

Hybrid-Matching für Live-Daten (07-03)

Kernaussage: datenListe findet Live-Daten (Skills/Mitarbeiter …) jetzt dreistufig — exakt/Token → semantisch → Selbstkorrektur — damit „Wer kann Kaffee kochen?“ den Skill „Kaffee machen“ findet (Nutzer-Befund 07-03; vorher Ganz-Phrasen-Substring → 0 Treffer).

  1. Token-Matching (server/src/assistant/matching.ts, rein/test:matching): ein Anfrage-Wort im Namen genügt; Ganz-Phrase bleibt Teilmenge (kein Regressionsrisiko).
  2. Semantisch: ohne Token-Treffer wird die Anfrage eingebettet (EU-Modell, wie RAG) und per Cosine gegen gecachte Embeddings der Skill-Namen gerankt (Schwelle 0.6, top 3). Die Vektoren leben transient im DO-Speicher (derived — nie persistiert, nicht Vectorize): nur Taxonomie-Namen, kein PII → RISK-17 unverändert. Treffer werden dem LLM transparent als „semantisch ähnlich“ markiert (Anti-Halluzination). Embedding-Aufrufe zählen in taktano_llm_tokens{zweck=embedding}.
  3. Selbstkorrektur: liefert auch das nichts, nennt das Werkzeug die verfügbaren Skill-Namen — das LLM matcht in der nächsten Tool-Runde selbst.

10. Bezug

OP-AI-4 (HANDOFF §4) · OP-DOCS-2 (Doku-Chatbot/Retrieval) · OP-DOCS-3 (MCP, gemeinsamer Index) · OP-DOCS-5 / OP-AUTH-1 / OP-AUDIT-1 (Berechtigung/Sichtbarkeit) · OP-DOCS-7 (Feedback-Schleife) · OP-AI-1 (Plan-/Konflikt-Narrative) · OP-OBS-1 (Observability) · R8-G2 (Anwenden/Verwerfen-Pfad).


↩ Zurück zur Doku-Landkarte · Lesepfade · Register (alle OPs)