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:
- Wie hoch ist die Auslastung?
- Wo stehen die Fahrzeuge? (physisch und in der Bearbeitung)
- Wann sind die nächsten freien Kapazitäten?
2. Drei Säulen
| # | Säule | Was | Quelle | Bezug |
|---|---|---|---|---|
| 1 | Doku-Q&A | „Wie funktioniert X?" | publizierte/Repo-Doku (Retrieval) | OP-DOCS-2 / -3 (gemeinsamer Index) |
| 2 | Feedback | Bug/Wunsch/Verbesserung im Dialog melden | Nutzereingabe → git-nah abgelegt | OP-DOCS-7 (Issue/Discussion-Schleife) |
| 3 | Insights | Auslastung · 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.
| Tool | Art | Liefert |
|---|---|---|
dokuSuche(frage) | read | relevante Doku-Passagen (Retrieval, berechtigungsgefiltert) |
auslastung(zeitraum) | read | Auslastung MA/Buchten (True-North 1) |
fahrzeugStandort(auftrag?) | read | physischer Slot + Bearbeitungsphase (True-North 2) |
freieKapazitaet(leistung?, ab?) | read | nä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) | simulate | Hebel + Konsequenz-Diff (konfliktHebel) — kein Apply |
feedbackAnlegen(text) | write (git-nah) | strukturiertes Issue/Discussion (OP-DOCS-7) |
issueSuche(frage) | read | offene GitHub-Issues zu einem Thema (Duplikate vermeiden) |
datenListe(was, filter?) | read | konkrete Live-Listen (Mitarbeiter/Aufträge/Skills/Leistungen/Buchten) |
datenmodell() | read | verbindliche 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 ausdesign/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ündeltendoku-index.generated.ts(kein externer Dienst nötig, deploy-sicher). Reiner Kern inserver/src/assistant/retrieval.ts(ein Kern, mehrere Frontends — OP-DOCS-2/-3); Re-Index beim Doku-Deploy (OP-DOCS-1 d) — Aktivierung siehewrangler.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 ohneTAKTANO_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 überassistent.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:pushje 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 Angebotsmodusscheduler/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.ts—snapshot()projiziert die bereits berechneten DTOs (auftraege,belegung) auf die schmaleInsightInput-Form und berechnetauslastung+fahrzeugStandortelive bei jedem Broadcast; Ergebnis als neues, additives FeldinsightsinLeitstandState. 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 typecheckgrün.
Slice 3 — Angular-Panel (read-only), umgesetzt (22.06.2026):
client/src/app/services/leitstand.service.ts—AuslastungInsight/StandortInsight/InsightsDTOzum Server-Vertrag gespiegelt, additives FeldinsightsinLeitstandState+EMPTY_STATE+ Selektorinsights.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-Gatecheck-no-hex), Patterns ausCOMPONENTS.md.- Eingebunden in
pages/overview.component.ts(Überblick-Raum) unter dem Produktionsfluss-Band. Verifiziert:npx ng build --configuration productiongrü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 ausdocs/**/*.md(scripts/build-doku-index.mjs→doku-index.generated.ts,npm run doku-index).server/party/leitstand.ts—onRequest-Endpoint/assistantam DO (Live-State + Engine am selben Ort, §4): bindet denToolKontextaninsights.ts,konfliktHebel(simulierend) und die Doku-Suche; dormant ohne LLM-Key (503). Verifiziert:npm run typecheckgrün;test:assistant-retrieval|-tools|-feedbackgrü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-MethodefeedbackAnlegen/erstelleFeedbackIssue(dormant ohneTAKTANO_FEEDBACK_TOKEN, Muster wiepullGithubKosten) + Audit-Eventassistent.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 (wieToastService), 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-Gatecheck-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 inshell.component.tsneben 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(inonRequest, kein LLM); die Issue- Erstellung ist inerstelleFeedbackIssue(): FeedbackErgebnisextrahiert 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-MapperfeedbackFehlerGrundinfeedback.ts(test:assistant-feedbackerweitert). Audit-Eventassistent.feedbackbleibt bei jedem Versuch. - Client:
feedback.service.tsPOSTet direkt/feedbackund quittiert ehrlich — Erfolgs-Toast mit Issue-Bezug bzw. Fehler-Toast mit echtem Grund (z. B. „HTTP 403 — Token-Scope"). Neuesfeedback-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-MethodesucheOffeneIssues(dormant ohneTAKTANO_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).
- OPs** (OPs via
- Assistent-Tools (
tools.ts):issueSuche(offene Issues nachschlagen / vor Feedback Duplikate vermeiden) unddatenListe(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 zumodel/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 inleitstand.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 · Dispatchdatenmodell→„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, inshell.component.tsgemountet): „📷 Screenshot aufnehmen" öffnet ein Ziehen-zum-Auswählen-Overlay; auf Loslassen wirdhtml2canvas-prolazy geladen (eigener Chunk; seit 02.07.2026 der gepflegte ESM-Fork statthtml2canvas— versteht moderne CSS-Farbfunktionen wiecolor()/oklch, an denen iPadOS/Safari die Aufnahme scheitern ließ), die Seite gerendert und auf die Region zugeschnitten →image/png-Blob. Overlay + Formular tragendata-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); auffreigegeben {blob, markierungen}lädt der Client das geschützte + annotierte Bild hoch. - Transport: neuer Outer-Fetch-Zweig
/api/feedback/screenshot(leitstand.ts): POST →FOTOS-Keyfeedback/<uuid>→{ r2Key }; dormant → 503 ohneFOTOS(Feedback filet weiter als Text). - Ans Issue:
FeedbackAnfrage(+ Client-Spiegel) trägtscreenshotKey?+markierungen?; der/feedback- Handler baut den Access-gated Link ausurl.origin,feedbackZuIssuehä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 viamitCorsauf die/parties/…- (Assistent/Feedback) und/api/feedback/screenshot-Antworten gelegt. Neue EnvASSISTENT_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 →
fuehreDialogbekommtwerkzeugFilter: ['dokuSuche','datenmodell'](keindatenListe/issueSuche/auslastung/… — keine Betriebsdaten übers Doku-Widget, OP-DOCS-5/OP-FEEDBACK-2) + einen System-Zusatz.internbleibttrue(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 →/assistantund 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.comundapp.taktano.comsind 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 vorapp.taktano.comfing 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 →fetchwarf → Widget zeigte pauschal „Server nicht erreichbar" (Nutzer-Befund 07-03). Fix: Same-Origin-Proxy, Slice 12. - Verifiziert:
test:cors·test:assistant-tools(Doku-Modus → nurdokuSuche+datenmodellerreichen das LLM) ·typecheck·docs-sitenpm 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 denrankVectorize-Vertrag; Chunk-Quelle = JSON-Sidecar ausnpm run doku-index, Build-Artefakt);[[vectorize]]-BindingDOKU_INDEXinwrangler.tomlaktiv; CI:deploy-Job pusht VORwrangler deploy,docs-deployre-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-errorim 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-BindingAPP_WORKERauf den Workertaktano(docs-site/wrangler.toml). Das Widget POSTet jetzt relativ (/parties/…aufdocs.taktano.com, Default-Origin leer indocusaurus.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 bestehendenapp.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, nurassistant·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 überASSISTENT_CORS_ORIGINS(werkzeugFilter: dokuSuche + datenmodell). Die CORS-Schicht (Slice 11) bleibt für echte Cross-Origin-Frontends (Preview/Staging viaTAKTANO_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 mitgesendeteCF_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-sitenpm run buildgrün (Widget/Config SSR-sicher) · Proxy-Pfad-Allowlist per Node-Snippet geprüft ·typecheckServer 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.ts—githubFeedbackListePfad(List-Issues-API,labels=feedback&state=all, jüngste Aktivität zuerst),issuesAusListe(PRs raus, Status sprechendoffen/geschlossen) undrouteAusIssueBody(liest die vom Anlegen strukturiert hinterlegte Verortung/Route aus dem Issue-Body zurück → „wo?" ohne zweites Datenfeld). Neuer read-only DO-EndpointGET /feedback/liste(FeedbackListe; dormant ohneTAKTANO_FEEDBACK_TOKEN, ehrlicher Grund; Berechtigung wie/assistant). DTO-Symmetrie:FeedbackIssue/FeedbackListein 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/listeerweitert). 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, bestehendesapp-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 tragendata-tk-screenshot-ignoreund landen nie im Bild. - Feedback-Overlay (App):
feedback-marker-overlay.component.ts— Marker am verorteten Element (Selektor wird per neuemselektorAusIssueBodyaus 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 buildgrü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 buildgrü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;escapeHtmlescapet jetzt auch"(Attribut-sicher). Nutzer-Blasen bleiben Klartext. - Server:
FeedbackIssueträgt zusätzlichdetails(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 ausfeedbackZuIssue) — reine HelferdetailsAusIssueBody/bilderAusIssueBodyingithub-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).
- Token-Matching (
server/src/assistant/matching.ts, rein/test:matching): ein Anfrage-Wort im Namen genügt; Ganz-Phrase bleibt Teilmenge (kein Regressionsrisiko). - 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}. - 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)