diff --git a/c-work/1-plan/2026-04-ai-reports-theming-and-pipeline.md b/c-work/1-plan/2026-04-ai-reports-theming-and-pipeline.md
new file mode 100644
index 0000000..2e8a377
--- /dev/null
+++ b/c-work/1-plan/2026-04-ai-reports-theming-and-pipeline.md
@@ -0,0 +1,614 @@
+
+
+
+
+# AI Reports: Generisches Style-Management, AI-Call-Konfiguration, Inline-Bilder
+
+## Beschreibung und Kontext
+
+Drei verzahnte Themen rund um AI-erzeugte Reports und AI-Calls in
+Workflows / Workspace:
+
+1. **Style-Management generisch und Agent-getrieben.** Die heutigen
+ Renderer (DOCX, XLSX, PPTX, HTML, PDF) haben hardcoded Farben (z.B.
+ DOCX-Tabellen-Header `4472C4`), feste Bildbreiten, hartes
+ `lang="en"` im HTML. Es gibt keinen Mechanismus, dass ein Agent
+ pro Bericht Styling, Logo, Schrift, Farben mitgeben kann. Wir
+ wollen **kein** fixes Template-/Theme-Mandate-CI-Konzept, sondern
+ ein **generisches Style-Schema**, das der Agent fuellt -- die
+ Renderer mappen daraus.
+2. **Per-Call AI-Konfiguration: Neutralisierung + erlaubte Modelle.**
+ Heute laufen AI-Calls aus Workflows ueber das zentrale AI-Gate
+ (`mainServiceAi.py`); Neutralisierung wird via Feature/Session/Request-
+ Flags gewaehlt, aber im FlowEditor ist sie pro Node nicht explizit
+ einstellbar. Eine **Whitelist erlaubter Modelle pro Node** existiert
+ ueberhaupt nicht. Im AI-Workspace soll beides automatisch aus dem
+ Workspace-Kontext kommen, im FlowEditor pro Node.
+3. **Inline-Bilder ueberall.** Die heutige MD->JSON-Pipeline
+ (`_mediaTools._markdownToDocumentJson`,
+ `subDocumentUtility.markdownToDocumentJson`) flacht alles auf Strings:
+ Bilder muessen eine eigene Markdown-Zeile sein. Ein Bild in einer
+ Tabellenzelle, mitten in einem Paragraph, in einer Listenzeile etc.
+ geht heute nicht.
+
+Zusaetzliche technische Schuld:
+
+- MD->JSON ist **doppelt** implementiert (`_mediaTools` und
+ `subDocumentUtility`) -- Drift-Risiko.
+- Agent ruft `renderReport` ohne `aiService` auf (`_mediaTools.py`
+ 282-287), KI-Style-Anpassung im Renderer-Pfad ist damit inaktiv. Das
+ wird durch den neuen Agent-Style-Pfad ohnehin obsolet.
+
+**Geschaeftstreiber:** Reports sind das visuelle Endprodukt, das User mit
+Kunden teilen. Ein Agent, der pro Bericht Layout-Entscheidungen trifft
+(Finanzreport vs. Marketing-Flyer vs. neutrales Memo), hebt die
+wahrgenommene Qualitaet deutlich. Per-Node-Neutralisierung +
+Modell-Whitelist machen Compliance und Kostenkontrolle pro Workflow
+explizit und auditierbar.
+
+## Fokus und kritische Details
+
+- Markdown bleibt User-/Agent-Eingangs-Schicht. JSON ist nur das interne
+ deterministisch erzeugte Zwischenformat. **Keine** Aenderung am MD-
+ First-Konzept.
+- Style-Schema: muss generisch genug sein, dass die heutigen Renderer
+ alle relevanten Visual-Konstanten daraus ziehen, aber **kein**
+ branchenspezifisches Preset-Inventar mit harter Auswahl.
+- Defaults sind wichtig: wenn der Agent nichts angibt, muss der Renderer
+ eine vernuenftige neutrale Default-Style-Set verwenden.
+- Inline-Bilder erfordern Schema-Aenderung beim `cell`/`paragraph`/
+ `bullet_list_item`-Content: weg von `string`, hin zu `inline-Run-
+ Liste` (`text` | `image` | `link` | `bold` | `italic`).
+- Per-Node AI-Konfiguration darf das **zentrale AI-Gate** nicht
+ umgehen: das Gate bleibt einzige Stelle, die Neutralisierung wirklich
+ durchsetzt. Node-Parameter werden in den `AiCallRequest` reingefuettert
+ und folgen den heutigen Praezedenzregeln (Feature -> Session -> Request,
+ OR-Logik fuer Neutralisierung).
+
+## Ziel und Nicht-Ziele
+
+- Ziel:
+ - **Style:** Generisches Style-Schema, vom Agent pro Render-Call
+ fuellbar (`metadata.style`). Renderer mappen einheitlich. Sensible
+ Defaults wenn nichts gesetzt.
+ - **AI-Call-Konfig:** Pro `ai.*`-Node im FlowEditor zwei neue Parameter
+ `requireNeutralization` (boolean) und `allowedModels` (Liste).
+ Workspace-Kontext fuellt sie automatisch fuer Workspace-Calls.
+ Validierung am AI-Gate (Whitelist-Check vor Modellwahl).
+ - **Inline-Bilder:** Bilder in Paragraph, Tabellenzelle, Listenzeile,
+ Code-Block-Caption etc. moeglich. MD-Syntax erkennt sie an
+ beliebiger Position. Renderer rendert sie inline wo das Format es
+ kann; sonst dokumentierter Fallback.
+ - **Konsolidierung:** 1× MD->JSON-Pipeline (Drift weg).
+- NICHT: Komplett neuer Renderer (z.B. WeasyPrint statt reportlab).
+- NICHT: Native XLSX-Charts via openpyxl ChartObjects (separater Plan).
+- NICHT: Mandate-CI-Konzept, Logo-Inheritance, Theme-Presets.
+- NICHT: User-konfigurierbare Custom-Themes via Admin-UI.
+- NICHT: Aenderung am bestehenden Neutralisierungs-Engine; nur Eingangs-
+ Pfad ergaenzen.
+
+## Betroffene Module
+
+- Gateway:
+ - `gateway/modules/serviceCenter/services/serviceAgent/coreTools/_mediaTools.py`
+ -- `_markdownToDocumentJson` raus, auf gemeinsamen Helper
+ umstellen; `renderDocument`-Tool-Schema ergaenzt um optionalen
+ `style`-Parameter.
+ - `gateway/modules/serviceCenter/services/serviceGeneration/subDocumentUtility.py`
+ -- alleinige MD->JSON-Funktion mit Inline-Run-Modell.
+ - `gateway/modules/serviceCenter/services/serviceGeneration/mainServiceGeneration.py`
+ -- `_resolveStyle(metadata.style) -> ResolvedStyle` (Defaults +
+ Agent-Overrides).
+ - `gateway/modules/serviceCenter/services/serviceGeneration/renderers/*`
+ -- alle 5 Renderer auf Style-Lookup umstellen, Inline-Run-Renderer.
+ - `gateway/modules/datamodels/datamodelJson.py` -- Schema-Erweiterung
+ fuer Inline-Runs und `cellContent`.
+ - `gateway/modules/datamodels/datamodelAi.py` --
+ `AiCallRequest.allowedModels: Optional[List[str]]` (Whitelist).
+ - `gateway/modules/serviceCenter/services/serviceAi/mainServiceAi.py`
+ -- Whitelist-Check vor Modellwahl, Fail-fast wenn keiner uebrig.
+ - `gateway/modules/features/graphicalEditor/nodeDefinitions/ai.py`
+ -- Standardparameter `requireNeutralization` + `allowedModels` zu
+ allen `ai.*`-Nodes (gemeinsamer Helper).
+ - `gateway/modules/workflows/methods/methodAi/actions/*.py` -- Node-
+ Parameter durchreichen in den `AiCallRequest`.
+ - `gateway/modules/serviceCenter/services/serviceAgent/conversationManager.py`
+ -- Workspace-Kontext-Defaults fuer `requireNeutralization` /
+ `allowedModels` aus Workspace-Config in `ServiceCenterContext`
+ setzen.
+- Frontend:
+ - FlowEditor: Pro AI-Node die zwei neuen Parameter im Properties-
+ Panel anzeigen (`requireNeutralization` Toggle, `allowedModels`
+ Multi-Select). Hilfetexte.
+ - Workspace-Settings: bestehender Neutralisierungs-Toggle bleibt;
+ neuer Multi-Select fuer `allowedModels` (Default: alle verfuegbaren
+ Modelle des Mandate).
+ - Style-Vorschau (klein): Optional Snippet im FlowEditor, das den
+ `style`-Parameter eines `file.create`/`renderDocument`-Nodes
+ visualisieren kann (deferred).
+- DB-Migration: nein im engeren Sinne. `Workflow`-Tabelle bekommt keinen
+ neuen Pflicht-Spalten (Node-Parameter leben im Graph-JSON).
+ `WorkspaceSettings.allowedModels`-Feld additiv via Auto-Init.
+
+## Style-Schema (generisch, Agent-getrieben)
+
+Der Agent uebergibt einen `style`-Block in `metadata` (alle Felder
+optional, Renderer fallen auf Defaults zurueck):
+
+```json
+{
+ "metadata": {
+ "language": "de",
+ "documents": [...],
+ "style": {
+ "fonts": {
+ "primary": "Calibri",
+ "monospace": "Consolas"
+ },
+ "colors": {
+ "primary": "#0F3D7A",
+ "secondary": "#1F2937",
+ "accent": "#E11D48",
+ "background": "#FFFFFF"
+ },
+ "headings": {
+ "h1": { "sizePt": 24, "weight": "bold", "color": "#0F3D7A", "spaceBeforePt": 12, "spaceAfterPt": 6 },
+ "h2": { "sizePt": 18, "weight": "bold", "color": "#0F3D7A" },
+ "h3": { "sizePt": 14, "weight": "bold" },
+ "h4": { "sizePt": 12, "weight": "bold" }
+ },
+ "paragraph": { "sizePt": 11, "lineSpacing": 1.15 },
+ "table": {
+ "headerBg": "#0F3D7A",
+ "headerFg": "#FFFFFF",
+ "rowBandingEven": "#F5F8FC",
+ "rowBandingOdd": "#FFFFFF",
+ "borderColor": "#CBD5E1",
+ "borderWidthPt": 0.5
+ },
+ "list": { "bulletChar": "•", "indentPt": 18 },
+ "image": { "defaultWidthPt": 480, "alignment": "center" },
+ "page": {
+ "format": "A4",
+ "marginsPt": { "top": 60, "bottom": 60, "left": 60, "right": 60 },
+ "showPageNumbers": true,
+ "headerHeight": 30,
+ "footerHeight": 30,
+ "headerLogo": { "fileId": "abc", "alignment": "right", "heightPt": 24 },
+ "headerText": "Quartalsbericht Q3/2026",
+ "footerText": "Vertraulich -- Seite {page}/{pages}"
+ }
+ }
+ }
+}
+```
+
+**Logo:** Es gibt keinen Mandate-Inheritance-Pfad. Wenn ein Logo in den
+Bericht soll, gibt der Agent eine `fileId` an (Datei muss zuvor via
+`generateImage`/`writeFile` oder DataSource-Download erzeugt sein und
+liegt bereits als `FileItem` vor).
+
+**Defaults (`gateway/modules/serviceCenter/services/serviceGeneration/styleDefaults.py`):**
+neutrale Default-Werte fuer alle Felder. `_resolveStyle(agentStyle) ->
+ResolvedStyle` macht ein deep-merge `defaults <- agentStyle`. Renderer
+sieht immer eine vollstaendig aufgeloeste Struktur.
+
+**Generische "Style-Komponenten" auf Renderer-Seite:** Pro visuellem
+Element (heading, paragraph, table, listItem, image, codeBlock, page)
+existiert eine Renderer-Funktion, die ausschliesslich die `ResolvedStyle`
+und den Content-Knoten konsumiert. So ist die Implementierung pro
+Format gleichfoermig (DOCX/XLSX/PPTX/HTML/PDF -- alle haben ein
+`renderHeading(headingNode, style.headings.hN)` etc.).
+
+## Inline-Run-Modell (fuer Inline-Bilder ueberall)
+
+Heute: `paragraph.text: str`, `cell: str`, `bullet_list.items: [str]`.
+
+Neu: `inlineRuns: List[InlineRun]` mit
+```json
+{ "type": "text" | "image" | "link" | "bold" | "italic" | "code",
+ "value": "...",
+ "fileId": "...", // bei type=image
+ "widthPt": 96, // optional bei type=image, default klein/inline
+ "href": "..." // bei type=link
+}
+```
+
+Backwards-kompatibel: Wenn der MD-Parser nur Text findet, bleibt es
+ein einzelner Run mit `type: text`. Renderer bekommen einen Helper
+`_renderInlineRuns(runs, style, container)`, der pro Run das richtige
+Element produziert (Text-Run mit Style, Inline-Image-Anchor,
+Hyperlink-Run, etc.).
+
+**Markdown-Erkennung:** `` und ``
+mitten in einem Paragraph oder in einer Tabellenzelle wird vom Parser
+zu einem `image`-Run. Listen-Items dasselbe.
+
+**Renderer-Faehigkeiten (Matrix):**
+
+| Format | Inline-Bild im Paragraph | Inline-Bild in Zelle | Inline-Bild in Listen-Item | Fallback |
+|--------|--------------------------|----------------------|-----------------------------|----------|
+| DOCX | nativ via `add_picture` | nativ via Cell-Paragraph | nativ | -- |
+| XLSX | Bild ueber Zelle anchorn | nativ Cell-Anchor | -- (kein Listen-Konzept) | Bild unter Zeile |
+| HTML | trivial `
` | trivial `
` in `
` | trivial ` ` in `` | -- |
+| PPTX | nicht nativ in Text-Run | begrenzt | -- | Bild als separate Shape unter Text |
+| PDF | reportlab Inline-Image | reportlab Cell-Image | begrenzt | Bild in eigenem Flowable unter Text |
+
+## AI-Call-Konfiguration pro Node (Neutralisierung + Modell-Whitelist)
+
+### Was es heute schon gibt (wichtig fuer Plan-Verstaendnis)
+
+- **`AiCallRequest.requireNeutralization`** existiert (`datamodelAi.py` Z. 177).
+- **`AiCallOptions.allowedProviders`** existiert (`datamodelAi.py` Z. 164) --
+ Provider-Whitelist auf Anbieter-Ebene (z.B. `anthropic`, `openai`,
+ `private-llm`).
+- **`Workflow.allowedProviders`** wird ueber `__getattr__` durchgereicht
+ (`mainServiceAi.py` Z. 89).
+- **`_calculateEffectiveProviders()`** macht RBAC ∩ Workflow.allowedProviders
+ und schreibt das in `request.options.allowedProviders` (`mainServiceAi.py`
+ Z. 175-178).
+- **`WorkspaceUserInput.allowedProviders`** + **`requireNeutralization`**
+ werden vom Frontend pro Request mitgegeben
+ (`routeFeatureWorkspace.py` Z. 111-113) und in den
+ `ServiceCenterContext` gesetzt (Z. 716-719).
+- **`ServiceCenterContext.requireNeutralization`** existiert (`context.py`
+ Z. 23).
+- **`GET /api/system/ai-models`** liefert die verfuegbaren Modelle
+ (`routeSystem.py` Z. 937).
+
+### Ziele dieses Plans (additiv zur bestehenden Provider-Logik)
+
+Wir fuehren `allowedModels` als **feinere Granularitaet neben** dem
+bestehenden `allowedProviders` ein -- nicht als Ersatz. Ein Provider
+darf erlaubt sein, aber nur bestimmte Modelle dieses Providers
+nutzbar (z.B. Provider `openai` erlaubt, aber nur `gpt-5-mini` nicht
+`gpt-5`).
+
+- **Pro `ai.*`-Node im FlowEditor (NEU):**
+ - `requireNeutralization` (`boolean`, Default `false`).
+ - `allowedModels` (`list[string]`, Default `[]` = "alle").
+ - (`allowedProviders` koennte spaeter analog folgen -- bewusst NICHT in
+ diesem Plan, weil heute nur Workflow-Level eingestellt wird.)
+- **Im AI-Workspace (Default-Persistenz):**
+ - `requireNeutralization`: heute schon im `WorkspaceUserInput` --
+ bleibt, jetzt aber **persistent** als User-Default.
+ - `allowedProviders`: heute schon im `WorkspaceUserInput` -- bleibt,
+ jetzt ebenfalls persistent als User-Default.
+ - `allowedModels` (NEU): zusaetzliches Feld in `WorkspaceUserInput` +
+ UI-Feld in den Workspace-Einstellungen.
+ - **Persistenz-Entscheidung (siehe Entscheidungs-Tabelle):** Phase 5a
+ kommt mit neuem Endpoint `GET/PUT /api/workspace/{instanceId}/user-
+ settings` fuer ALLE DREI Felder als User-spezifische Defaults pro
+ Workspace -- damit der User die Settings nicht bei jedem Browser-
+ Open neu setzen muss. Frontend laedt Defaults beim Workspace-Open
+ und schickt sie pro Call mit.
+- **Datenmodell-Erweiterung:**
+ - `AiCallOptions.allowedModels: Optional[List[str]] = None` (analog
+ zu `allowedProviders`).
+ - `Workflow.allowedModels: Optional[List[str]] = None` (analog zu
+ `Workflow.allowedProviders`).
+- **Validierung am zentralen AI-Gate (`mainServiceAi.py`):**
+ - `requireNeutralization` folgt **bestehender** OR-Logik (keine
+ Aenderung).
+ - Neue private Funktion `_calculateEffectiveModels()` analog zu
+ `_calculateEffectiveProviders()`: RBAC ∩ Workflow.allowedModels ∩
+ Node.allowedModels (Node setzt es ueber `request.options`).
+ - **Effektive Filterkette:** Provider-Filter (heute) AND Model-Filter
+ (neu) AND functionCall-Anforderung (heute).
+ - Ist die gefilterte Liste leer -> Fail-fast mit
+ `RuntimeError("No allowed model available for this call (allowedModels=[...], allowedProviders=[...])")`.
+
+### Beispiel: AI-Node-Definition (gekuerzt)
+
+```python
+# in gateway/modules/features/graphicalEditor/nodeDefinitions/ai.py
+# (allowedProviders existiert bereits auf Workflow-Ebene; hier nur die
+# zwei NEUEN Felder pro AI-Node)
+_AI_COMMON_PARAMS = [
+ {"name": "requireNeutralization", "type": "boolean", "required": False,
+ "frontendType": "checkbox", "default": False,
+ "description": t("Eingaben fuer diesen Call neutralisieren")},
+ {"name": "allowedModels", "type": "array", "required": False,
+ "frontendType": "modelMultiSelect", "default": [],
+ "description": t("Erlaubte LLM-Modelle (leer = alle erlaubten); zusaetzlicher AND-Filter ueber allowedProviders")},
+]
+
+# pro Node anhaengen, z.B. ai.prompt:
+{
+ "id": "ai.prompt",
+ ...
+ "parameters": [
+ {"name": "aiPrompt", ...},
+ {"name": "resultType", ...},
+ {"name": "documentList", ...},
+ {"name": "simpleMode", ...},
+ *_AI_COMMON_PARAMS,
+ ],
+ ...
+}
+```
+
+### Datenfluss
+
+```mermaid
+flowchart LR
+ Node[ai.* Node Parameters] -->|"requireNeutralization, allowedModels"| Action[methodAi/actions/*.py]
+ Action -->|"sets request.options.allowedModels + request.requireNeutralization"| Gate[mainServiceAi._call]
+ Gate --> EffMod[_calculateEffectiveModels RBAC AND Workflow AND Node]
+ Gate --> EffProv[_calculateEffectiveProviders existing]
+ EffMod --> Pick[_pickModel]
+ EffProv --> Pick
+ Pick -->|"empty?"| Fail[Fail-fast RuntimeError]
+ Pick -->|"ok"| Neutral[_shouldNeutralize OR-Logik existing]
+ Neutral --> LLM[Modell-Call]
+ WorkspaceUI[WorkspaceUserInput requireNeutralization+allowedModels] -->|"per-request"| Ctx[ServiceCenterContext]
+ WorkspaceSet[GET/PUT /workspace/.../user-settings NEU] -->|"defaults"| WorkspaceUI
+```
+
+## Entscheidungen
+
+| Datum | Entscheidung | Begruendung |
+|-------|-------------|------------|
+| 2026-04-29 | KEIN fixer Theme-Katalog, KEIN Mandate-CI-Konzept | User-Korrektur: generisches Style-Management vom Agent gefuellt |
+| 2026-04-29 | Style-Schema generisch in `metadata.style`, Defaults zentral | Agent kann pro Bericht entscheiden; Renderer haben einheitliche Mapping-Schicht |
+| 2026-04-29 | Inline-Run-Modell statt String-basierter Content | Voraussetzung fuer "Bilder ueberall" |
+| 2026-04-29 | Per-Node `requireNeutralization` + `allowedModels` als Standardparameter ALLER `ai.*`-Nodes | Keine Sonder-Felder pro Node-Typ; im FlowEditor konsistent |
+| 2026-04-29 | `allowedModels` ergaenzt das bestehende `allowedProviders`, ersetzt es nicht | Provider = Anbieter-Ebene (existiert), Model = konkrete Modell-Ebene (neu, feiner) |
+| 2026-04-29 | `allowedModels` lebt in `AiCallOptions` (analog `allowedProviders`), NICHT direkt in `AiCallRequest` | Symmetrie zur bestehenden Provider-Whitelist |
+| 2026-04-29 | Neuer Workspace-User-Settings-Endpoint `GET/PUT /api/workspace/{instanceId}/user-settings` fuer ALLE DREI Felder (`requireNeutralization`, `allowedProviders`, `allowedModels`) | Heute werden `requireNeutralization`/`allowedProviders` pro Request aus dem Frontend mitgeliefert -- ohne Persistenz muesste der User die Settings bei jedem Browser-Open neu setzen. Konsistenz-Argument: wenn `allowedModels` persistiert wird, dann auch die anderen zwei -- sonst zwei Default-Mechanismen mit unterschiedlichem Verhalten. |
+| 2026-04-29 | `ServiceCenterContext.requireNeutralization` ist bereits gesetzt; analoge `allowedModels`-Propagation NICHT noetig (Frontend schickt es ohnehin pro Call) | Kontextfeld nur dort, wo Sub-Service ohne Frontend-Input es braucht |
+| 2026-04-29 | Whitelist-Check vor Modellwahl; leer -> Fail-fast | Compliance: Workflow soll **deterministisch** scheitern statt stillschweigend ein nicht-erlaubtes Modell zu nehmen |
+| 2026-04-29 | MD->JSON-Konsolidierung in `subDocumentUtility.py` | Bereits zentralerer Pfad als `_mediaTools` |
+| 2026-04-29 | Native XLSX-Charts via openpyxl spaeter | Heute reicht PNG-Embed; Chart-API ist eigene Komplexitaet |
+
+## Umsetzungs-Checkliste
+
+### Phase 1 -- MD->JSON konsolidieren + Inline-Runs
+
+- [ ] `subDocumentUtility.markdownToDocumentJson` zur einzigen Quelle
+ machen.
+- [ ] `_mediaTools._markdownToDocumentJson` durch Aufruf des Helpers
+ ersetzen, lokale Funktion entfernen.
+- [ ] Schema-Erweiterung in `datamodels/datamodelJson.py`:
+ - `paragraph.inlineRuns: List[InlineRun]` (alt `text: str` als
+ Backwards-Compat-Feld weiter unterstuetzt).
+ - `bullet_list.items: List[List[InlineRun]]`.
+ - `table.cell` neu strukturiert: `cellContent: List[InlineRun]`
+ oder `cellBlocks: List[Section]` (fuer komplexere Zellen mit
+ eigenem Paragraph/Image-Block).
+- [ ] MD-Parser im konsolidierten Helper:
+ - Inline `` an beliebiger Position erkennen ->
+ `image`-Run.
+ - Inline `[text](url)` -> `link`-Run.
+ - `**bold**` / `*italic*` / `` `code` `` -> entsprechende Runs.
+ - Tabellen-Zellen: erkennen ob nur Text -> einzelner Text-Run;
+ sonst voll geparst (inkl. Bild).
+- [ ] Helper `_renderInlineRuns(runs, style, container)` als gemeinsame
+ Spec; pro Renderer eigene Implementierung.
+
+### Phase 2 -- Style-System
+
+- [ ] Defaults-Modul `gateway/modules/serviceCenter/services/serviceGeneration/styleDefaults.py`
+ mit neutralen Default-Werten fuer alle Style-Felder.
+- [ ] Style-Resolver in `mainServiceGeneration.py`:
+ `_resolveStyle(metadataStyle: dict | None) -> ResolvedStyle`
+ (deep-merge defaults <- agentStyle).
+- [ ] Renderer auf Resolver umstellen: kein Renderer hat mehr eigene
+ Default-Konstanten.
+- [ ] `renderDocument`-Tool (`_mediaTools.py`) Schema:
+ - Optionaler Parameter `style` (object) wird in `metadata.style`
+ gemappt.
+ - Tool-Beschreibung dokumentiert das Schema mit Beispielen.
+
+### Phase 3 -- Renderer-Refactor (generische Style-Komponenten)
+
+- [ ] DOCX (`rendererDocx.py`):
+ - `_createTableRowXml` (~ 644-697) Header-Farbe/-Schrift aus
+ `style.table`.
+ - `_renderJsonImage` (~ 1016-1082) Default-Breite aus
+ `style.image.defaultWidthPt`.
+ - Headings aus `style.headings.hX`.
+ - Inline-Run-Renderer fuer Paragraph/Cell.
+ - Page-Margins / Header-Logo / Footer-Text aus `style.page`.
+ - `templateName: corporate|minimal` deprecaten (1 Release lang
+ warnen, dann raus).
+- [ ] XLSX (`rendererXlsx.py`):
+ - `_renderJsonImage` Bildbreite aus `style.image`.
+ - Tabellen-Header aus `style.table`.
+ - Inline-Bilder per Cell-Anchor (siehe Phase 4).
+- [ ] PPTX (`rendererPptx.py`):
+ - Slide-Size aus `style.page`.
+ - Title/Body aus `style.headings`.
+ - Inline-Bilder als zusaetzliche Shape (Fallback).
+- [ ] HTML (`rendererHtml.py`):
+ - `lang` aus `metadata.language`.
+ - Inline-CSS aus `style` generiert (`_generateCssStyles`
+ umbauen).
+ - Inline-Bilder als ` `.
+- [ ] PDF (`rendererPdf.py`):
+ - Sample-Style-Set aus `style`.
+ - Page-Margins aus `style.page.marginsPt`.
+ - Inline-Bilder als reportlab Inline-Image.
+
+### Phase 4 -- Inline-Bilder ueberall (Renderer-Spezifika)
+
+- [ ] DOCX: Cell-Image via `cell.add_paragraph().add_run().add_picture(...)`.
+- [ ] DOCX/PDF: Inline-Image im Paragraph als Run-mit-Image.
+- [ ] XLSX: `openpyxl.drawing.image.Image` mit Cell-Anchor; pro Bild eine
+ neue Image-Instance (openpyxl-Constraint).
+- [ ] HTML: ` ` direkt in `` / ` ` / ` ` -- trivial.
+- [ ] PPTX: Inline-in-Text-Run nicht moeglich -> Image als zusaetzliche
+ Shape unter dem Text-Frame, mit Warning im JSON-Output
+ (`_renderingFallback: pptx-image-below-text`).
+- [ ] Tests pro Format: 1× Inline-Bild im Paragraph, 1× in
+ Tabellenzelle, 1× in Listen-Item.
+
+### Phase 5 -- AI-Call-Konfiguration pro Node (Datenmodell + Backend)
+
+- [ ] `gateway/modules/datamodels/datamodelAi.py`:
+ - `AiCallOptions.allowedModels: Optional[List[str]] = None`
+ (analog zu `allowedProviders` in Z. 164 -- KEIN neues Feld in
+ `AiCallRequest`).
+- [ ] `gateway/modules/datamodels/datamodelAuto.py` (oder wo immer das
+ `Workflow`-Modell liegt):
+ - `Workflow.allowedModels: Optional[List[str]] = None` (analog
+ `Workflow.allowedProviders`).
+- [ ] `gateway/modules/serviceCenter/services/serviceAi/mainServiceAi.py`:
+ - Neue private Methode `_calculateEffectiveModels()` analog zu
+ `_calculateEffectiveProviders()` (Z. 175-178). RBAC-Modelle ∩
+ `Workflow.allowedModels` ∩ `request.options.allowedModels`.
+ - In `_call`/`_callStream`/`callEmbedding` analog zur
+ Provider-Logik aufrufen und in `request.options.allowedModels`
+ schreiben.
+ - `_pickModel` filtert anschliessend AND-verknuepft auf Provider
+ UND Model-Whitelist.
+ - Fail-fast `RuntimeError` wenn die Filterkette leer wird, mit
+ Audit-Log-Zeile (modelle, provider, rbac-permitted, workflow,
+ node).
+- [ ] `__getattr__` in `mainServiceAi.py` (Z. 88-90) erweitern:
+ Tuple-Liste um `"allowedModels"` ergaenzen (damit
+ `service.allowedModels` analog `service.allowedProviders`
+ aus dem Workflow durchgereicht wird).
+
+### Phase 5a -- Workspace User-Settings (Persistenz fuer alle drei Felder)
+
+- [ ] Neue persistente Settings pro User × Workspace-Instanz.
+ Tabelle `WorkspaceUserSettings` (additiv via Auto-Init):
+ - `instanceId UUID NOT NULL`
+ - `userId UUID NOT NULL`
+ - `requireNeutralization BOOL DEFAULT false`
+ - `allowedProviders JSONB DEFAULT '[]'`
+ - `allowedModels JSONB DEFAULT '[]'`
+ - PK / Unique: (`instanceId`, `userId`).
+- [ ] Backend-Endpoints in `routeFeatureWorkspace.py`:
+ - `GET /api/workspace/{instanceId}/user-settings` -- effektive
+ Settings fuer current user (Auto-Insert mit Defaults wenn
+ leer).
+ - `PUT /api/workspace/{instanceId}/user-settings` -- speichern
+ (alle drei Felder im Body).
+- [ ] `WorkspaceUserInput.allowedModels: List[str] = []` (additiv,
+ `routeFeatureWorkspace.py` Z. 109-114). `requireNeutralization`
+ und `allowedProviders` sind dort schon vorhanden (Z. 112-113).
+- [ ] In `_runWorkspaceAgent` (Z. 693+): `allowedModels` ebenso
+ durchreichen wie heute `allowedProviders` (Z. 716-717) -- in das
+ `request.options.allowedModels`-Feld schreiben (siehe Phase 5
+ Datenmodell).
+- [ ] Frontend Workspace:
+ - Workspace-Settings-Tab/Panel mit allen drei Feldern:
+ - Toggle `requireNeutralization`.
+ - Multi-Select `allowedProviders` (Optionen aus
+ `GET /api/system/ai-models` -> distinct connectorTypes).
+ - Multi-Select `allowedModels` (Optionen aus
+ `GET /api/system/ai-models` -> displayNames).
+ - Beim Workspace-Open: Settings via
+ `GET .../user-settings` laden, Defaults in den Workspace-State
+ setzen.
+ - "Speichern"-Button im Settings-Panel ruft `PUT .../user-
+ settings` mit allen drei Feldern.
+ - Bei jedem Workspace-Call: aktuelle Settings als
+ `WorkspaceUserInput`-Felder mitgeben (alle drei).
+
+### Phase 5b -- AI-Nodes im FlowEditor
+
+- [ ] `gateway/modules/features/graphicalEditor/nodeDefinitions/ai.py`:
+ - Modul-lokaler Helper `_AI_COMMON_PARAMS = [...]` mit den 2
+ neuen Parametern.
+ - In ALLE 8 Eintraege von `AI_NODES` als `*_AI_COMMON_PARAMS`
+ anhaengen.
+- [ ] `gateway/modules/workflows/methods/methodAi/actions/*.py`:
+ - In jeder Action (`process.py`, `webResearch.py`,
+ `summarizeDocument.py`, `translateDocument.py`,
+ `convertDocument.py`, `generateDocument.py`,
+ `generateCode.py`, `consolidate.py`):
+ - `requireNeutralization = parameters.get("requireNeutralization")`
+ -> in `request.requireNeutralization` setzen.
+ - `allowedModels = parameters.get("allowedModels", [])` -> in
+ `request.options.allowedModels` setzen.
+ - Helper-Funktion `_applyCommonAiParams(parameters, request)` in
+ `methodAi/_common.py` (neu) zum Reduzieren von Boilerplate.
+- [ ] FlowEditor (Frontend):
+ - Neuer `frontendType: "modelMultiSelect"` -- holt verfuegbare
+ Modelle ueber `GET /api/system/ai-models` und stellt
+ Multi-Select dar.
+ - Frontend-Generic-Form-Generator unterstuetzt diesen Type
+ (Properties-Panel pro AI-Node).
+
+### Phase 6 -- Trustee-Templates anpassen (entkoppelt vom Bau-Plan B)
+
+- [ ] In `mainTrustee.py`-Templates: `style`-Block in `aiPrompt`-Node
+ Parametern setzen (Finanzreport-Defaults: dunkelblaue Headings,
+ konservatives Calibri etc.) -- damit Trustee out-of-the-box gut
+ aussieht.
+- [ ] `requireNeutralization: true` falls Trustee-Daten sensibel
+ (entscheidet Compliance-Doku, Default empfohlen).
+- [ ] `allowedModels` pro Trustee-Workflow optional pinned (z.B. nur
+ private LLM fuer Finance-Daten).
+
+### Phase 7 -- Tests / Snapshots
+
+- [ ] Pro Format ein Snapshot-Test mit Custom-Style:
+ `test_render_with_agent_style_.py`.
+- [ ] Inline-Bilder-Tests pro Format (siehe Phase 4).
+- [ ] AI-Gate-Tests:
+ `test_allowed_models_filter.py` (Whitelist greift),
+ `test_allowed_models_empty_fails.py` (Fail-fast),
+ `test_workspace_propagates_neutralization.py`.
+
+## Akzeptanzkriterien
+
+| # | Kriterium (Given-When-Then) | Prio |
+|---|-----------------------------|------|
+| 1 | Given Agent ruft `renderDocument` mit `style.colors.primary="#E11D48"`, When DOCX rendert, Then Headings nutzen diese Farbe | must |
+| 2 | Given Agent ruft `renderDocument` ohne `style`-Block, When Renderer arbeitet, Then werden neutrale Defaults verwendet (kein Crash) | must |
+| 3 | Given Markdown enthaelt `Bei der Sitzung  wurde besprochen ...`, When DOCX gerendert, Then erscheint das Bild inline mitten im Paragraph | must |
+| 4 | Given Markdown-Tabellenzelle `\|  \|`, When DOCX/HTML/XLSX gerendert, Then Bild liegt in der Zelle | must |
+| 5 | Given Markdown-Listen-Item mit Inline-Bild, When HTML gerendert, Then Bild im `` | should |
+| 6 | Given PPTX gerendert mit Inline-Bild im Paragraph, When Output inspiziert, Then Bild ist als separate Shape unterhalb mit Warning im Run-Log | should |
+| 7 | Given AI-Node mit `requireNeutralization: true`, When Workflow laeuft, Then Prompt/Context werden im AI-Gate neutralisiert (Beweis im Audit-Log) | must |
+| 8 | Given AI-Node mit `allowedModels: ["claude-4-7-opus"]`, When Workflow laeuft und nur dieses Modell verfuegbar, Then Call benutzt es | must |
+| 9 | Given AI-Node mit `allowedModels: ["nichtExistent"]`, When Workflow laeuft, Then Fail-fast mit Error-Message inkl. RBAC/Workflow/Node-Filter-Snapshot | must |
+| 10 | Given Workspace mit gespeicherten User-Settings `requireNeutralization=true, allowedProviders=["openai"], allowedModels=["gpt-5-mini"]`, When User Workspace neu oeffnet, Then sind ALLE drei Settings vorausgefuellt und werden bei jedem Call mitgeschickt | must |
+| 11 | Given Workspace mit `requireNeutralization: true`, When Sub-Agent-AI-Tools laufen, Then alle Calls werden neutralisiert (ServiceCenterContext propagiert) | must |
+| 12 | Given Provider-Whitelist `allowedProviders=["openai"]` UND Model-Whitelist `allowedModels=["claude-4-7-opus"]`, When Filterung laeuft, Then leere Liste -> Fail-fast (AND-Logik, nicht OR) | must |
+| 13 | Given alte Implementierungen `_mediaTools._markdownToDocumentJson`, When neu gebaut, Then ruft sie nur noch den Helper aus `subDocumentUtility` auf | must |
+
+## Testplan
+
+| ID | AC | Art | Automatisiert | Repo-Pfad | Status |
+|----|----|-----|--------------|-----------|--------|
+| T1 | 1,2 | snapshot | ja | gateway/tests/serviceGeneration/test_agent_style_docx.py | pending |
+| T2 | 3 | integration | ja | gateway/tests/serviceGeneration/test_inline_image_paragraph.py | pending |
+| T3 | 4 | integration | ja | gateway/tests/serviceGeneration/test_inline_image_table_cell.py | pending |
+| T4 | 5 | integration | ja | gateway/tests/serviceGeneration/test_inline_image_list_item_html.py | pending |
+| T5 | 6 | integration | ja | gateway/tests/serviceGeneration/test_inline_image_pptx_fallback.py | pending |
+| T6 | 7 | integration | ja | gateway/tests/serviceAi/test_node_neutralization_flag.py | pending |
+| T7 | 8,9 | integration | ja | gateway/tests/serviceAi/test_allowed_models_whitelist.py | pending |
+| T8 | 10 | integration | ja | gateway/tests/features/workspace/test_user_settings_persistence.py | pending |
+| T9 | 11 | integration | ja | gateway/tests/serviceAi/test_workspace_propagates_settings.py | pending |
+| T10 | 12 | unit | ja | gateway/tests/serviceAi/test_effective_models_and_providers.py | pending |
+| T11 | 13 | unit | ja | gateway/tests/serviceCenter/test_md_to_json_consolidation.py | pending |
+| T12 | -- | unit | ja | frontend_nyla/src/components/flowEditor/__tests__/AiNodeProperties.test.tsx | pending |
+
+## Links
+
+- Aktuelle Pipeline-Architektur: Subagent-Report 2026-04-29.
+- Renderer-Code: `gateway/modules/serviceCenter/services/serviceGeneration/renderers/`.
+- MD->JSON-Code:
+ `gateway/modules/serviceCenter/services/serviceAgent/coreTools/_mediaTools.py`,
+ `gateway/modules/serviceCenter/services/serviceGeneration/subDocumentUtility.py`.
+- AI-Gate: `gateway/modules/serviceCenter/services/serviceAi/mainServiceAi.py`.
+- Neutralisierung: `wiki/b-reference/platform/neutralization.md`.
+- Wiki: `wiki/b-reference/gateway/ai-agent.md`,
+ `wiki/b-reference/gateway/agent-file-bridge.md`,
+ `wiki/b-reference/gateway/workflow.md`.
+
+## Abschluss
+
+- [ ] `wiki/b-reference/gateway/ai-agent.md` -- Style-System + per-call
+ Konfiguration ergaenzen
+- [ ] `wiki/b-reference/gateway/workflow.md` -- AI-Node-Standardparameter
+ dokumentieren
+- [ ] `wiki/b-reference/platform/neutralization.md` -- per-Node-Trigger
+ ergaenzen (Praezedenzkette: Feature -> Workspace -> Node -> Request)
+- [ ] `wiki/TOPICS.md` ggf. neuer Eintrag "AI-Node-Konfiguration"
+- [ ] Dieses Dokument -> `z-archive/` verschoben
diff --git a/c-work/1-plan/2026-04-comcoach-greenfield-ia.md b/c-work/1-plan/2026-04-comcoach-greenfield-ia.md
new file mode 100644
index 0000000..891fd32
--- /dev/null
+++ b/c-work/1-plan/2026-04-comcoach-greenfield-ia.md
@@ -0,0 +1,249 @@
+
+
+
+
+# ComCoach Greenfield-IA: TrainingModule + Sessions
+
+## Beschreibung und Kontext
+
+Die ComCoach-UI ist gewachsen und chaotisch:
+
+- 4 Sidebar-Eintraege (`dashboard`, `coaching`, `dossier`, `settings`),
+ aber `coaching` und `dossier` rendern die **identische** Komponente
+ `CommcoachDossierView`
+ (`frontend_nyla/src/pages/FeatureView.tsx` 168-173).
+- RBAC-Definition kennt nur 3 UI-Keys
+ (`mainCommcoach.py` 19-34) -> Inkonsistenz mit Sidebar.
+- Das Dashboard verlinkt mit `?context={id}`, die Dossier-View liest
+ diesen Query-Param **nicht** -> falscher Default-Kontext nach Klick
+ (`CommcoachDossierView.tsx` 142-146).
+- CRUD fuer "Coachings" (heute `CoachingContext`) ist unvollstaendig:
+ Backend hat `PUT/DELETE /contexts/{id}`, UI bietet nur Create + Archive.
+- Statistik wird doppelt gezeigt (Dashboard + Settings).
+- Die Datenstruktur ist heute **flach**: `CoachingContext -> Sessions`,
+ ohne Modul/Reihen-Konzept. Das passt nicht zum gewuenschten Use-Case
+ "ein Psychologe (Modul) macht m Sessions zu einem Thema, alle tragen zu
+ Modul-KPI bei" oder "E-Learning-Modul + Pruefungs-Modul".
+
+**Geschaeftstreiber:** User finden sich nicht zurecht -> ComCoach wird
+unterbenutzt. Mit klarer Modul-of-Sessions-IA und 5 sauberen Tabs wird
+ComCoach erst zum vermarktbaren Produkt.
+
+## Fokus und kritische Details
+
+- Bestehende Daten (alle aktuellen `CoachingContext`-Eintraege) muessen
+ migriert werden -> 1:1 Rename auf `TrainingModule` mit Default
+ `moduleType="coaching"`.
+- "Module" und "Coaching" sind im UI gleichwertig zu verwenden -- intern
+ ist es immer ein `TrainingModule`. Im UI je nach `moduleType` das
+ Label anpassen ("Coaching", "Training", "Pruefung", "E-Learning").
+- KeepAlive-Mechanismus
+ (`frontend_nyla/src/pages/views/commcoach/CommcoachKeepAlive.tsx`)
+ beibehalten -- die Voice-Session darf nicht gekillt werden bei
+ Sidebar-Wechsel.
+
+## Ziel und Nicht-Ziele
+
+- Ziel: 5-Tab-IA (Dashboard, Assistent, Module, Session, Einstellungen)
+ mit klarer Use-Case-Zuordnung. Datenmodell `TrainingModule` mit KPIs +
+ N Sessions.
+- NICHT: Voice-Pipeline aendern.
+- NICHT: Persona-Marketplace (eigener Plan).
+- NICHT: Multi-Tenant-Sharing von Modulen ueber Mandate-Grenzen
+ (eigener Plan, falls je gewollt).
+
+## Betroffene Module
+
+- Gateway:
+ - `gateway/modules/features/commcoach/datamodelCommcoach.py` --
+ `CoachingContext` -> `TrainingModule` (Rename + neue Felder
+ `kpiTargets jsonb`, `moduleType enum`, `goals text`).
+ - `gateway/modules/features/commcoach/interfaceFeatureCommcoach.py` --
+ Methoden umbenennen (intern), DB-Migration anwenden.
+ - `gateway/modules/features/commcoach/routeFeatureCommcoach.py` --
+ Routes umbenennen `/contexts` -> `/modules` (oder Alias-Periode).
+ - `gateway/modules/features/commcoach/mainCommcoach.py` -- RBAC-Keys
+ auf neue Tab-Struktur (5 Keys: dashboard, assistant, modules,
+ session, settings).
+- Frontend:
+ - `frontend_nyla/src/types/mandate.ts` 277-286 -- Sidebar 4 -> 5
+ Eintraege.
+ - `frontend_nyla/src/pages/FeatureView.tsx` 168-173 -- View-Mapping
+ aktualisieren.
+ - `frontend_nyla/src/pages/views/commcoach/` -- bestehende
+ `CommcoachDossierView` zerlegen in:
+ - `CommcoachDashboardView` (existiert, KPIs ergaenzen).
+ - `CommcoachAssistantView` (NEU): Wizard-Flow neues Modul + erste
+ Session.
+ - `CommcoachModulesView` (NEU): Liste, CRUD, Sessions je Modul.
+ - `CommcoachSessionView` (NEU, ehem. coaching-Tab): aktive
+ Session.
+ - `CommcoachSettingsView` (existiert, Statistik raus).
+ - `frontend_nyla/src/api/commcoachApi.ts` -- API-Wrapper aktualisieren.
+ - `frontend_nyla/src/pages/views/commcoach/CommcoachKeepAlive.tsx` --
+ Komponenten-Mapping aktualisieren (Session bleibt KeepAlive).
+- DB-Migration: ja -- Rename `CoachingContext` -> `TrainingModule`,
+ additive Felder.
+- RBAC: ja -- 5 statt 3 UI-Keys.
+
+## Zieldatenmodell
+
+```mermaid
+flowchart LR
+ Inst[FeatureInstance commcoach] --> Mod[TrainingModule]
+ Mod --> Sess[Session]
+ Sess --> Msg[Message]
+ Sess --> Score[Score]
+ Mod --> Task[Task]
+ Mod --> Kpi[ModuleKpi]
+ Mod --> Per[Persona ref]
+```
+
+`TrainingModule` Felder (Auszug):
+
+| Feld | Typ | Bedeutung |
+|------|-----|-----------|
+| `id` | UUID | PK |
+| `instanceId` | UUID | FeatureInstance-Scope |
+| `userId` | UUID | Owner |
+| `mandateId` | UUID | Tenant |
+| `title` | text | "Konfliktgespraeche", "JS-Grundkurs" |
+| `moduleType` | enum | `coaching` \| `training` \| `exam` \| `elearning` |
+| `personaId` | UUID? | Default-Persona fuer Sessions |
+| `goals` | text | freie Zielbeschreibung |
+| `kpiTargets` | jsonb | strukturierte Ziel-KPIs |
+| `status` | enum | `active` \| `archived` \| `completed` |
+| `createdAt` / `updatedAt` | timestamp | -- |
+
+## IA: 5 Tabs (final)
+
+| # | Tab | Zweck | Komponente |
+|---|------|-------|-------------|
+| 1 | Dashboard | Gamification, Streak, Levels, aktive Module-Karten, Tipp des Tages, Schnell-Aktion "Neues Modul" | `CommcoachDashboardView` |
+| 2 | Assistent | Wizard: Modul-Typ -> Thema -> Persona -> KPIs -> "Erste Session starten" | `CommcoachAssistantView` |
+| 3 | Module | CRUD-Liste aller Module (Filter nach Status/Typ), pro Modul aufklappbare Sessions | `CommcoachModulesView` |
+| 4 | Session | aktive/laufende Session als Single-Page (TTS, Voice, Anhaenge, Agent-Aktivitaet, Abschliessen/Abbrechen). Erreichbar nur ueber Modul oder Assistent | `CommcoachSessionView` |
+| 5 | Einstellungen | Stimme/Sprache, Erinnerungen, E-Mail-Zusammenfassung. KEINE Statistik mehr | `CommcoachSettingsView` |
+
+## Use-Case-Mapping
+
+| Use-Case | Tab(s) | Hauptaktion |
+|----------|--------|-------------|
+| 1 Gamification-Dashboard | Dashboard | KPIs + Streak ansehen |
+| 2 Assistent: neues Coaching | Assistent -> Session | Wizard durchklicken |
+| 3 Session-Operations | Session | TTS, Voice, Tasks waehrend Session |
+| 4 Listen CRUD | Module | Module pflegen, Sessions je Modul aufklappen |
+| 5 Instanzen-Einstellungen | Einstellungen | Stimme, Erinnerungen |
+
+## Entscheidungen
+
+| Datum | Entscheidung | Begruendung |
+|-------|-------------|------------|
+| 2026-04-29 | Backend-Entity heisst neu `TrainingModule` (Rename von `CoachingContext`) | Generischer Begriff deckt Coaching, Training, Exam, ELearning ab |
+| 2026-04-29 | Im UI je nach `moduleType` Labelt "Coaching/Training/..." | UX-Klarheit |
+| 2026-04-29 | Sidebar 5 Tabs (statt 4 mit Doppelung) | Use-Cases brauchen klare Trennung Assistent vs. Liste |
+| 2026-04-29 | Statistik im Settings-Tab raus, nur Dashboard | Doppelung weg |
+| 2026-04-29 | KeepAlive bleibt nur fuer Session-Tab (nicht Module/Dashboard) | Voice-Session muss persistieren, Listen koennen unmounten |
+
+## Umsetzungs-Checkliste
+
+### Phase 1 -- Backend Datenmodell + Routes
+
+- [ ] `datamodelCommcoach.py`:
+ - Klasse `CoachingContext` -> `TrainingModule` (Rename, alle
+ FKs in Childs `contextId` -> `moduleId`).
+ - Neue Felder `kpiTargets jsonb`, `moduleType enum`, `goals text`.
+- [ ] DB-Migration: `script_db_rename_coaching_context_to_training_module.py`.
+ Tabellenname Postgres-Rename, FK-Spalte `contextId` -> `moduleId`
+ auf allen abhaengigen Tabellen (`CoachingSession`,
+ `CoachingTask`, `CoachingScore`).
+- [ ] `interfaceFeatureCommcoach.py` -- Methodennamen umstellen
+ (`createContext` -> `createModule` etc.) mit camelCase intern.
+- [ ] `routeFeatureCommcoach.py`:
+ - Neue Pfade `/api/commcoach/{instanceId}/modules/...`.
+ - Alias-Periode: alte `/contexts/...`-Routen zeigen 301 auf neue
+ Routen (1 Release lang), dann raus.
+- [ ] `mainCommcoach.py`:
+ - RBAC-Keys: `dashboard`, `assistant`, `modules`, `session`,
+ `settings`.
+ - Templates fuer Standard-Modulvorlagen optional (z.B. "Konflikt-
+ Coaching", "Sprach-Training").
+
+### Phase 2 -- Frontend Routing & Sidebar
+
+- [ ] `frontend_nyla/src/types/mandate.ts` 277-286 ComCoach-Eintraege
+ auf 5 Eintraege umstellen.
+- [ ] `frontend_nyla/src/App.tsx` 185-187 Routes ergaenzen (`assistant`,
+ `modules`, `session`).
+- [ ] `frontend_nyla/src/pages/FeatureView.tsx` 168-173 View-Mapping
+ auf neue Komponenten.
+- [ ] KeepAlive nur noch fuer `session`-Tab konfigurieren.
+
+### Phase 3 -- Frontend Komponenten
+
+- [ ] `CommcoachDashboardView` -- KPI-Karten verfeinern (pro Modul-Typ
+ Aggregat), neuer "Modul anlegen"-CTA.
+- [ ] NEU `CommcoachAssistantView` -- Wizard 4 Steps (Typ/Thema/Persona/
+ KPIs) + "Erste Session starten" -> Navigation `session?moduleId=...`.
+- [ ] NEU `CommcoachModulesView` -- Liste, Filter (`moduleType`,
+ Status), CRUD-Dialoge (Edit/Delete vorhanden, jetzt im UI), pro
+ Zeile aufklappbar Sessions inkl. Sessions-CRUD (zumindest Delete).
+- [ ] NEU `CommcoachSessionView` -- aus existierender `Dossier`-Tab-
+ Logik die "coaching"-Tab-Inhalte rausziehen.
+- [ ] `CommcoachSettingsView` -- Statistik-Sektion entfernen.
+- [ ] `CommcoachDossierView` ENTFERNEN nach Migration aller Inhalte.
+
+### Phase 4 -- Bugfixes (Vor-Ort)
+
+- [ ] `?context={id}` auf neuen Param `?moduleId={id}` umstellen, in
+ allen Views konsistent lesen.
+- [ ] `commcoachApi.ts` Wrapper `updateContextApi`, `deleteContextApi`,
+ `activateContextApi`, `updateTaskApi` aus Backend nutzen (heute
+ ungenutzt).
+
+### Phase 5 -- Doku
+
+- [ ] Neue b-reference `wiki/b-reference/gateway/features/commcoach.md`
+ anlegen.
+- [ ] `wiki/TOPICS.md` Eintrag "ComCoach Architecture".
+
+## Akzeptanzkriterien
+
+| # | Kriterium (Given-When-Then) | Prio |
+|---|-----------------------------|------|
+| 1 | Given Sidebar ComCoach, When User es oeffnet, Then sieht er 5 Tabs (Dashboard, Assistent, Module, Session, Einstellungen) | must |
+| 2 | Given Tab Assistent, When User Wizard durchklickt, Then ist am Ende ein neues Modul + eine erste Session aktiv | must |
+| 3 | Given Tab Module, When User auf "Bearbeiten" eines Moduls klickt, Then kann er Titel, Typ, Persona, Goals, KPIs editieren | must |
+| 4 | Given Tab Module, When User auf "Loeschen" eines Moduls klickt, Then ist das Modul + alle Sessions weg (mit Confirm) | must |
+| 5 | Given Tab Dashboard, When User auf eine Modul-Karte klickt, Then landet er im Tab Module mit dem richtigen Modul aufgeklappt (`moduleId`-Param wird gelesen) | must |
+| 6 | Given Tab Einstellungen, When User es oeffnet, Then sieht er KEINE Statistik-Sektion mehr | should |
+| 7 | Given Bestand-CoachingContext-Eintraege, When Migration laeuft, Then heissen sie alle `TrainingModule` mit `moduleType=coaching` und alle FKs zeigen korrekt | must |
+| 8 | Given Voice-Session laeuft im Tab Session, When User auf Tab Module wechselt und zurueck, Then laeuft die Session weiter (KeepAlive) | should |
+
+## Testplan
+
+| ID | AC | Art | Automatisiert | Repo-Pfad | Status |
+|----|----|-----|--------------|-----------|--------|
+| T1 | 1 | unit | ja | frontend_nyla/src/types/__tests__/mandate.test.ts | pending |
+| T2 | 2 | e2e | ja | frontend_nyla/tests/e2e/commcoach-assistant.spec.ts | pending |
+| T3 | 3,4 | e2e | ja | frontend_nyla/tests/e2e/commcoach-modules-crud.spec.ts | pending |
+| T4 | 5 | e2e | ja | frontend_nyla/tests/e2e/commcoach-dashboard-link.spec.ts | pending |
+| T5 | 6 | unit | ja | frontend_nyla/src/pages/views/commcoach/__tests__/CommcoachSettingsView.test.tsx | pending |
+| T6 | 7 | integration | ja | gateway/tests/features/commcoach/test_migration_rename.py | pending |
+| T7 | 8 | manual | nein | -- | pending |
+
+## Links
+
+- Audit-Quelle: Subagent-Report 2026-04-29.
+- Aktueller Code:
+ `gateway/modules/features/commcoach/`,
+ `frontend_nyla/src/pages/views/commcoach/`.
+- Aktuelles Konzept (veraltet):
+ `gateway/modules/features/commcoach/CONCEPT.md`.
+
+## Abschluss
+
+- [ ] `wiki/b-reference/gateway/features/commcoach.md` neu anlegen
+- [ ] `wiki/TOPICS.md` Eintrag "ComCoach"
+- [ ] `gateway/modules/features/commcoach/CONCEPT.md` aktualisieren
+- [ ] Dieses Dokument -> `z-archive/` verschoben
diff --git a/c-work/1-plan/2026-04-teamsbot-greenfield-ia-and-live-update.md b/c-work/1-plan/2026-04-teamsbot-greenfield-ia-and-live-update.md
new file mode 100644
index 0000000..b92a5ae
--- /dev/null
+++ b/c-work/1-plan/2026-04-teamsbot-greenfield-ia-and-live-update.md
@@ -0,0 +1,274 @@
+
+
+
+
+# TeamsBot Greenfield-IA: MeetingModule + Live-Update-Fixes
+
+## Beschreibung und Kontext
+
+Die TeamsBot-UI ist aktuell:
+
+- 3 Tabs (Dashboard "Uebersicht", Sitzungen, Einstellungen).
+- Kein Meeting-Entity -- alles ist `TeamsbotSession.meetingLink`. Wer
+ z.B. ein wiederkehrendes Weekly-Standup ueber Wochen verfolgen will,
+ muss die Sessions per Datum suchen.
+- Kein "Modul"-Konzept fuer Reihen (wie der User wuenscht).
+- Live-Updates haengen an SSE in `TeamsbotSessionView.tsx` mit
+ `useEffect`-Deps `[instanceId, sessionId]` only (Z. 327-328) +
+ `eslint-disable` -- reagiert daher NICHT zuverlaessig auf
+ Status-Wechsel und auf neue `agentRun`-Events. Letztere sind nur
+ `_dlog`, NICHT in der UI sichtbar.
+- `stats`-Payload aus `getSession`-API
+ (`routeFeatureTeamsbot.py` 357-358) wird vom Frontend ignoriert.
+- Dashboard-Counts haben 10s Lag (Polling).
+
+**Geschaeftstreiber:** User finden sich nicht zurecht; Live-Bedienung
+wirkt fragil ("zeigt das hier ueberhaupt was Aktuelles?"). Mit Modul-
+of-Sessions-IA (analog ComCoach) und sauberen Live-Update-Pfaden wird
+TeamsBot zum verlaesslichen Meeting-Tool.
+
+## Fokus und kritische Details
+
+- **Live-Update ist UX-kritisch** -- Session-State, agentRun-Indicator
+ und Stats muessen waehrend laufender Meetings sichtbar pulsieren.
+- SSE-`useEffect`-Deps nicht naiv aufschnueren -- sonst Reconnect-
+ Flicker bei jedem Status-Bit-Wechsel. Loesung: dedizierter Reconnect-
+ Hook, der nur reconnektet wenn (a) sessionId wechselt oder (b) Session
+ von "terminal" zurueck zu "active" geht (sehr selten).
+- Backend `stats`-Payload bereits vorhanden, nur nicht gebunden.
+- `MeetingModule` ist additiv -- alte `TeamsbotSession`-Eintraege
+ bekommen `moduleId` per Migration (Default-Modul "Adhoc").
+
+## Ziel und Nicht-Ziele
+
+- Ziel:
+ - 5-Tab-IA (Dashboard, Assistent, Module, Live-Session, Einstellungen).
+ - `MeetingModule`-Entity (Reihe + KPIs + Default-Bot + Default-
+ Director-Prompts).
+ - 4 Live-Update-Fixes (siehe unten F-fix-1 .. F-fix-4).
+- NICHT: Aenderung an Bot-Bridge (.NET Media-Bridge bleibt).
+- NICHT: WebSocket-Migration im Frontend (SSE bleibt, ist Architektur-
+ Entscheid).
+- NICHT: Multi-Mandate-Sharing von Modulen.
+
+## Betroffene Module
+
+- Gateway:
+ - `gateway/modules/features/teamsbot/datamodelTeamsbot.py` -- neue
+ Klasse `TeamsbotMeetingModule`, FK `TeamsbotSession.moduleId`
+ (nullable in Uebergangsphase).
+ - `gateway/modules/features/teamsbot/interfaceFeatureTeamsbot.py` --
+ CRUD fuer `MeetingModule`.
+ - `gateway/modules/features/teamsbot/routeFeatureTeamsbot.py` --
+ neue Routen `/modules/...`, `agentRun`-Event sauberer schicken,
+ `stats`-Payload kontinuierlich anreichern.
+ - `gateway/modules/features/teamsbot/mainTeamsbot.py` -- RBAC-Keys
+ auf 5 Tabs, optional Default-MeetingModule-Templates.
+- Frontend:
+ - `frontend_nyla/src/types/mandate.ts` 244-252 -- TeamsBot-Sidebar
+ 3 -> 5 Eintraege.
+ - `frontend_nyla/src/pages/FeatureView.tsx` 159-163 -- View-Mapping
+ aktualisieren.
+ - `frontend_nyla/src/pages/views/teamsbot/` -- vorhandene Komponenten
+ aufteilen:
+ - `TeamsbotDashboardView` -- KPIs erweitern (Modul-Aggregate +
+ Gamification).
+ - `TeamsbotAssistantView` (NEU) -- Wizard.
+ - `TeamsbotModulesView` (NEU) -- Modul-CRUD + Sessions je Modul.
+ - `TeamsbotSessionView` -- bleibt, jetzt nur Live-Session.
+ - `TeamsbotSettingsView` -- bleibt.
+ - `frontend_nyla/src/api/teamsbotApi.ts` -- Module-API + neuer
+ Reconnect-Hook.
+- DB-Migration: ja (additive Tabelle + nullable FK).
+
+## Zieldatenmodell
+
+```mermaid
+flowchart LR
+ Inst[FeatureInstance teamsbot] --> Mod[MeetingModule]
+ Mod --> Sess[TeamsbotSession meetingLink]
+ Sess --> Trans[Transcript]
+ Sess --> Resp[BotResponse]
+ Sess --> Dir[DirectorPrompt]
+ Mod --> Kpi[ModuleKpi/Goals]
+ Mod --> DefBot[Default Bot]
+ Mod --> DefDir[Default Director Prompts]
+```
+
+`MeetingModule` Felder (Auszug):
+
+| Feld | Typ | Bedeutung |
+|------|-----|-----------|
+| `id` | UUID | PK |
+| `instanceId` | UUID | FeatureInstance-Scope |
+| `mandateId` | UUID | Tenant |
+| `ownerUserId` | UUID | Owner |
+| `title` | text | "Weekly Standup", "Q3 Sales Review-Reihe" |
+| `seriesType` | enum | `weekly` \| `biweekly` \| `monthly` \| `adhoc` \| `project` |
+| `defaultBotId` | UUID? | `TeamsbotSystemBot` |
+| `defaultDirectorPrompts` | jsonb | Liste vorausgefuellter Director-Prompts |
+| `goals` | text | -- |
+| `kpiTargets` | jsonb | -- |
+| `status` | enum | `active` \| `archived` \| `completed` |
+
+## IA: 5 Tabs (final)
+
+| # | Tab | Zweck | Komponente |
+|---|------|-------|-------------|
+| 1 | Dashboard | Gamification (Talking-Time, Director-Trefferquote, beliebteste Prompts), aktive Module/Sessions, Quick-Action "Neues Meeting" | `TeamsbotDashboardView` |
+| 2 | Assistent | Wizard: Modul waehlen oder neu -> Meeting-Link/Auto-Join -> Bot starten | `TeamsbotAssistantView` |
+| 3 | Module | CRUD-Liste aller Meeting-Module, pro Modul aufklappbare Session-Liste | `TeamsbotModulesView` |
+| 4 | Live-Session | aktive Sitzung: Regie-Panel, UDB-Sidebar, Transkript, Antworten, Bot-Status, MFA, Stats-Karten | `TeamsbotSessionView` (refactor) |
+| 5 | Einstellungen | Bot-Account, Stimme, System-Bots (SysAdmin) | `TeamsbotSettingsView` |
+
+## Use-Case-Mapping
+
+| Use-Case | Tab(s) | Hauptaktion |
+|----------|--------|-------------|
+| 1 Gamification-Dashboard | Dashboard | Talking-Time + Modul-KPIs |
+| 2 Assistent: neues Meeting | Assistent -> Live-Session | Wizard durchklicken |
+| 3 Session-Operations | Live-Session | Stop, Director-Prompts, MFA, Stats |
+| 4 Listen CRUD | Module | Module + Sessions je Modul pflegen |
+| 5 Instanzen-Einstellungen | Einstellungen | Bot, Stimme, System-Bots |
+
+## Live-Update-Fixes (parallel zur IA)
+
+### F-fix-1: SSE-Reconnect-Hook
+
+- Problem: `useEffect` `[instanceId, sessionId]`-only haengt nicht an
+ `session.status` -> Reconnect bei legitimen Status-Wechseln nicht
+ garantiert.
+- Loesung: Eigener Hook `useTeamsbotSessionStream(instanceId, sessionId,
+ isTerminal)` der intern reconnectet wenn `isTerminal` von `true` ->
+ `false` flippt (z.B. nach manuellem Re-Start einer beendeten Session).
+ Andere Status-Bits triggern KEINEN Reconnect.
+
+### F-fix-2: agentRun-Event sichtbar
+
+- Problem: `agentRun`-SSE-Events sind nur `_dlog`
+ (`TeamsbotSessionView.tsx` 290-293).
+- Loesung: Status-Bubble in der Live-View ("Agent denkt nach: ...")
+ als kurzlebige animierte Komponente.
+
+### F-fix-3: stats-Karten
+
+- Problem: `getSession`-API liefert `stats` (Sprechminuten, Bot-
+ Antworten, Latenz) -- Frontend ignoriert.
+- Loesung: Header-Karten in `TeamsbotSessionView` gebunden an
+ `session.stats`. Aktualisierung kommt automatisch via SSE
+ `sessionState`-Events (Backend muss `stats` mitliefern -- ggf.
+ Backend-Anpassung in `routeFeatureTeamsbot.py`).
+
+### F-fix-4: Dashboard-Counts ohne 10s-Lag
+
+- Problem: Dashboard pollt `listSessions` alle 10s.
+- Option A (kleiner Eingriff): bei aktiver Session 3s-Polling, sonst
+ 30s.
+- Option B (besser): SSE-Channel pro Mandate
+ `/api/teamsbot/{instanceId}/dashboard/stream` mit "session-tick"-
+ Events (Counter-Updates push, kein Polling).
+- **Default:** Option A in Phase 4, Option B als Follow-up.
+
+## Entscheidungen
+
+| Datum | Entscheidung | Begruendung |
+|-------|-------------|------------|
+| 2026-04-29 | Neues `MeetingModule`-Entity additiv | Reihen-Konzept gewuenscht; bestehende Sessions in "Adhoc"-Modul |
+| 2026-04-29 | 5 Tabs (statt 3) | Use-Cases brauchen klare Trennung Assistent/Module/Live |
+| 2026-04-29 | SSE bleibt (kein WS) | Architektur stabil, Browser-Bot nutzt WS, Frontend nicht |
+| 2026-04-29 | Dashboard-Counts: Adaptive Polling jetzt, SSE-Channel als Follow-up | Pragmatisch, schnell sichtbar |
+
+## Umsetzungs-Checkliste
+
+### Phase 1 -- Backend Datenmodell + Routes
+
+- [ ] `datamodelTeamsbot.py`:
+ - Neue Klasse `TeamsbotMeetingModule`.
+ - `TeamsbotSession.moduleId Optional[UUID]` additiv.
+- [ ] DB-Migration:
+ `script_db_init_teamsbot_meeting_modules.py` -- legt Tabelle an,
+ erzeugt pro `instanceId` ein Default-`Adhoc`-Modul, setzt
+ bestehende Sessions auf dieses Modul.
+- [ ] `interfaceFeatureTeamsbot.py` -- CRUD-Methoden fuer Module.
+- [ ] `routeFeatureTeamsbot.py`:
+ - Neue Routen `GET/POST/PUT/DELETE /modules/...`.
+ - `getSession` liefert `stats` (existiert), `listSessions`
+ ergaenzt `moduleId` im Payload.
+ - SSE: `sessionState`-Event traegt jetzt auch `stats`.
+ - SSE: `agentRun`-Event sauberer schicken (toolName, status:
+ `started`/`finished`/`failed`).
+
+### Phase 2 -- Backend Live-Stats anreichern
+
+- [ ] `_buildSessionStats(session) -> SessionStats` zentral, in
+ jedem `sessionState`-SSE-Event aufrufen (nicht nur on-demand).
+
+### Phase 3 -- Frontend Routing & Sidebar
+
+- [ ] `frontend_nyla/src/types/mandate.ts` 244-252 TeamsBot-Eintraege
+ auf 5 Eintraege.
+- [ ] `frontend_nyla/src/App.tsx` Routes ergaenzen (`assistant`,
+ `modules`).
+- [ ] `frontend_nyla/src/pages/FeatureView.tsx` View-Mapping
+ aktualisieren.
+
+### Phase 4 -- Frontend Komponenten
+
+- [ ] NEU `TeamsbotAssistantView` -- Wizard 3 Steps (Modul-
+ Auswahl/Neu, Meeting-Link, Bot-Auswahl) -> "Bot starten" ->
+ Navigation `live-session?sessionId=...`.
+- [ ] NEU `TeamsbotModulesView` -- Liste, CRUD, aufklappbare Sessions.
+- [ ] `TeamsbotSessionView` Refactor:
+ - Reconnect-Hook (F-fix-1) integrieren.
+ - Status-Bubble fuer agentRun (F-fix-2).
+ - Stats-Karten im Header binden (F-fix-3).
+- [ ] `TeamsbotDashboardView`:
+ - Polling adaptiv (F-fix-4 Option A).
+ - Modul-Aggregate-Sektion ergaenzen (Top-Module nach
+ Session-Count, Talking-Time-Verteilung etc.).
+
+### Phase 5 -- Doku
+
+- [ ] `wiki/b-reference/teams-bot/architecture.md` Module-Sektion
+ ergaenzen, Live-Update-Pfade dokumentieren.
+- [ ] `wiki/TOPICS.md` ggf. Eintrag "TeamsBot Module".
+
+## Akzeptanzkriterien
+
+| # | Kriterium (Given-When-Then) | Prio |
+|---|-----------------------------|------|
+| 1 | Given Sidebar TeamsBot, When User es oeffnet, Then sieht er 5 Tabs (Dashboard, Assistent, Module, Live-Session, Einstellungen) | must |
+| 2 | Given Tab Module, When User auf "Neues Modul" klickt, Then kann er Titel, Series-Typ, Default-Bot, Director-Prompts, KPIs setzen | must |
+| 3 | Given Modul existiert, When User auf "Neue Session" klickt, Then wird eine `TeamsbotSession` mit `moduleId` angelegt und im Live-Tab geoeffnet | must |
+| 4 | Given Live-Session laeuft, When der Backend-Agent ein Tool aufruft, Then erscheint im UI ein Status-Bubble "Agent denkt nach" mit Tool-Name | must |
+| 5 | Given Live-Session laeuft, When sich Sprechminuten/Bot-Antworten/Latenz aendern, Then aktualisieren die Stats-Karten ohne Page-Reload | must |
+| 6 | Given Session terminiert (z.B. Stop), When User auf gleicher Seite bleibt, Then schliesst die SSE sauber, kein Reconnect-Loop | must |
+| 7 | Given Bestand-Sessions ohne Modul, When Migration laeuft, Then sind alle Sessions im Default-`Adhoc`-Modul je Instanz einsortiert | must |
+| 8 | Given Dashboard offen, When eine aktive Session laeuft, Then aktualisieren die Counter spaetestens nach 3s | should |
+| 9 | Given keine aktive Session, When Dashboard offen, Then aktualisiert es nur alle 30s | should |
+
+## Testplan
+
+| ID | AC | Art | Automatisiert | Repo-Pfad | Status |
+|----|----|-----|--------------|-----------|--------|
+| T1 | 1 | unit | ja | frontend_nyla/src/types/__tests__/mandate.test.ts | pending |
+| T2 | 2,3 | e2e | ja | frontend_nyla/tests/e2e/teamsbot-modules-crud.spec.ts | pending |
+| T3 | 4 | unit | ja | frontend_nyla/src/pages/views/teamsbot/__tests__/TeamsbotSessionView_agentRun.test.tsx | pending |
+| T4 | 5 | integration | ja | frontend_nyla/tests/integration/teamsbot-stats-binding.spec.ts | pending |
+| T5 | 6 | manual | nein | -- | pending |
+| T6 | 7 | integration | ja | gateway/tests/features/teamsbot/test_migration_modules.py | pending |
+| T7 | 8,9 | manual | nein | -- | pending |
+
+## Links
+
+- Audit-Quelle: Subagent-Report 2026-04-29.
+- Aktueller Code: `gateway/modules/features/teamsbot/`,
+ `frontend_nyla/src/pages/views/teamsbot/`.
+- Wiki: `wiki/b-reference/teams-bot/architecture.md`,
+ `wiki/c-work/4-done/2026-04-teamsbot-director-prompts.md`.
+
+## Abschluss
+
+- [ ] `wiki/b-reference/teams-bot/architecture.md` aktualisiert
+- [ ] `wiki/TOPICS.md` ggf. neuer Eintrag
+- [ ] Dieses Dokument -> `z-archive/` verschoben
diff --git a/c-work/1-plan/2026-04-trustee-budget-comparison-refactor.md b/c-work/1-plan/2026-04-trustee-budget-comparison-refactor.md
new file mode 100644
index 0000000..50f45bd
--- /dev/null
+++ b/c-work/1-plan/2026-04-trustee-budget-comparison-refactor.md
@@ -0,0 +1,163 @@
+
+
+
+
+# Trustee Budget Comparison Refactor (Excel-only, Prompt-Klarheit)
+
+## Beschreibung und Kontext
+
+Der Trustee-Service "Budget-Vergleich"
+(`gateway/modules/features/trustee/mainTrustee.py` 430-461) liefert heute
+ein Word-Dokument mit teils kaputten Bildern in Tabellenzellen und mit
+Charts pro Konto statt einem Uebersichts-Chart. Ursachen:
+
+- Workflow-Template hat **kein** `resultType` -- das Ausgabeformat wird vom
+ Modell entschieden, oft DOCX.
+- Prompt (Z. 442-454) ist mehrdeutig: "Erstelle **ein** Abweichungs-Chart"
+ steht direkt neben "**pro Konto**" -- das LLM waehlt mal Lesart A, mal B.
+- Bilder-in-Tabellenzellen sind im Markdown->JSON-Pfad
+ (`gateway/modules/serviceCenter/services/serviceAgent/coreTools/_mediaTools.py`
+ 72-86, 108-127) **nicht** vorgesehen: Tabellenzellen sind reine
+ Strings, Bilder muessen eine eigene Markdown-Zeile sein. python-docx
+ koennte Bilder in Zellen, aber unsere MD-Pipeline kann es nicht
+ ausdruecken.
+
+**User-Entscheid:** Excel ist der natuerliche Container fuer einen
+Soll/Ist-Konten-Vergleich (tabellarische Daten, eingebettete Charts,
+sortier-/filterbar). Word-Pfad fuer diesen Workflow weg.
+
+## Fokus und kritische Details
+
+- Format-Festlegung darf NICHT vom LLM ueberschrieben werden -- Workflow
+ muss `resultType: "xlsx"` hart setzen.
+- Prompt-Refactor: ein Chart, ueber alle Konten. Few-Shot-Beispiel mit
+ Zielstruktur direkt im Prompt einbauen.
+- "Bild in Tabellenzelle"-Thema gehoert zum generellen Renderer-Refactor
+ (siehe `2026-04-ai-reports-theming-and-pipeline.md`); hier nur den
+ Budget-spezifischen Effekt heilen, indem das Layout
+ Tabelle-darunter-Chart ist (kein Bild in Zelle noetig).
+
+## Ziel und Nicht-Ziele
+
+- Ziel: Budget-Vergleich liefert konsistent EINE .xlsx mit
+ Tabelle (alle Konten) + 1 Uebersichts-Chart + Management-Summary.
+- NICHT: Bilder-in-Tabellenzellen technisch loesen (gehoert zu D).
+- NICHT: native XLSX-Charts via openpyxl (Default bleibt PNG-Embed). Kann
+ in spaeteren Iteration nachgezogen werden.
+- NICHT: Aenderungen am `refreshAccountingData`-Node oder
+ `ai.process`-Action.
+
+## Betroffene Module
+
+- Gateway:
+ - `gateway/modules/features/trustee/mainTrustee.py` -- Budget-Template
+ Z. 430-461 (Prompt + `resultType` + Output-Schema-Hinweis).
+- Frontend:
+ - `frontend_nyla/src/pages/views/trustee/TrusteeAnalyseView.tsx` --
+ File-Karten korrekt rendern (xlsx-Icon).
+- DB-Migration: nein (nur Template-Aenderung; bei naechstem
+ `_copyTemplateWorkflows`-Lauf greift es).
+
+## Entscheidungen
+
+| Datum | Entscheidung | Begruendung |
+|-------|-------------|------------|
+| 2026-04-29 | Excel ist primaeres Format, Word-Pfad fuer Budget-Vergleich entfernt | Daten-tabellarisch, Charts in Excel native passend |
+| 2026-04-29 | Charts via PNG-Embed (kein openpyxl-native), 1 Chart ueber alle Konten | Schon implementiert, kein neuer Renderer-Pfad noetig |
+| 2026-04-29 | Bilder-in-Zellen wird NICHT in diesem Plan geloest | Layout Tabelle-darunter-Chart reicht; generischer Fix in D |
+
+## Umsetzungs-Checkliste
+
+### Phase 1 -- Workflow-Template
+
+- [ ] In `mainTrustee.py` Budget-Template:
+ - Parameter `resultType: "xlsx"` setzen.
+ - Parameter `documentTheme: "finance"` setzen (Vorbereitung fuer
+ Plan D, hat heute noch keinen Effekt).
+- [ ] `_copyTemplateWorkflows` (`interfaceFeatures.py` 269-338) prueft
+ idempotent ob bestehende User-Workflows mit ID `trustee-budget-
+ comparison` aktualisiert werden -- falls nicht, separates
+ Migrations-Skript-Stub anlegen.
+
+### Phase 2 -- Prompt klarziehen
+
+- [ ] Neuer Prompt-Text in `mainTrustee.py`:
+
+```text
+Fuehre einen Budget-Soll/Ist-Vergleich durch und liefere EIN Excel-Dokument
+mit folgender Struktur:
+
+1. Tabelle "Konten-Vergleich" -- EINE Tabelle, EINE Zeile pro Konto:
+ Spalten: Konto-Nr | Konto-Name | Soll | Ist | Abweichung absolut |
+ Abweichung % | Status (OK / Warnung / Kritisch).
+2. EINE Visualisierung "Soll vs. Ist gesamt" -- ein einziges
+ Balkendiagramm UNTER der Tabelle, das ALLE Konten in einer Grafik
+ gegenueberstellt (gruppierte Balken: Soll und Ist je Konto).
+3. Kurzer Management-Summary-Absatz (3-5 Saetze) UNTER dem Chart
+ mit den 3 groessten Abweichungen (>10%) und einer fachlichen
+ Einschaetzung.
+
+Verwende die uebergebene Budget-Datei als Soll-Quelle und die im
+Kontext bereitgestellten Buchhaltungsdaten als Ist-Quelle.
+WICHTIG: Erstelle KEINEN separaten Chart pro Konto. Nur EIN
+Uebersichts-Chart ueber alle Konten ist gewuenscht.
+```
+
+- [ ] Few-Shot-Beispiel als zweites System-Block-Append: minimaler
+ JSON-Stub mit `metadata.documents[0].sections = [table, image,
+ paragraph]`.
+
+### Phase 3 -- Excel-Renderer-Sicht
+
+- [ ] Verifizieren dass `rendererXlsx` PNG-Bilder unter Tabellen
+ korrekt rendert (vorhanden, `_renderJsonImage`-Pfad).
+- [ ] Default-Bildbreite checken (heute hardcoded `maxWidth=800` in
+ `gateway/modules/serviceCenter/services/serviceGeneration/renderers/rendererXlsx.py`
+ ~ 1427-1428) -- ok fuer A4-druckbares Diagramm; spaeter via
+ Theme-System (Plan D) konfigurierbar.
+
+### Phase 4 -- Frontend
+
+- [ ] `TrusteeAnalyseView.tsx` Budget-Tab: Hinweistext "Excel-Bericht"
+ statt generisch "Datei", File-Icon `description` statt generisch.
+- [ ] Wenn Plan A2 vorher gemerged ist: "Im Workspace ansehen"-Button.
+
+### Phase 5 -- Test
+
+- [ ] Smoke-Test: 1 Run mit Demo-Budget-File -> 1 .xlsx mit
+ genau einer `table`-Section + einer `image`-Section + einer
+ `paragraph`-Section.
+- [ ] Snapshot des produzierten .xlsx in
+ `gateway/tests/features/trustee/snapshots/`.
+
+## Akzeptanzkriterien
+
+| # | Kriterium (Given-When-Then) | Prio |
+|---|-----------------------------|------|
+| 1 | Given Budget-Vergleich-Run, When er fertig ist, Then liefert er genau eine .xlsx (kein .docx) | must |
+| 2 | Given die .xlsx wird geoeffnet, When User die erste Tabelle sieht, Then sind alle Konten als Zeilen vorhanden | must |
+| 3 | Given die .xlsx, When User runter scrollt, Then findet er EIN Uebersichts-Chart (nicht eines pro Konto) | must |
+| 4 | Given die .xlsx, When User zum Ende scrollt, Then steht ein Management-Summary-Absatz | should |
+| 5 | Given Budget-Vergleich-Karte im Trustee-Dashboard, When der File-Link erscheint, Then zeigt das UI ein Excel-Icon und Label "Excel-Bericht" | should |
+
+## Testplan
+
+| ID | AC | Art | Automatisiert | Repo-Pfad | Status |
+|----|----|-----|--------------|-----------|--------|
+| T1 | 1 | integration | ja | gateway/tests/features/trustee/test_budget_comparison_format.py | pending |
+| T2 | 2,3,4 | integration | ja | gateway/tests/features/trustee/test_budget_comparison_structure.py | pending |
+| T3 | 5 | unit | ja | frontend_nyla/src/pages/views/trustee/__tests__/TrusteeAnalyseView.test.tsx | pending |
+
+## Links
+
+- Workflow-Definition: `gateway/modules/features/trustee/mainTrustee.py`
+ 430-461.
+- Renderer-Pipeline: `gateway/modules/serviceCenter/services/serviceGeneration/`.
+- Verwandter Plan: `wiki/c-work/1-plan/2026-04-ai-reports-theming-and-pipeline.md`.
+- Audit-Quelle: Subagent-Report 2026-04-29.
+
+## Abschluss
+
+- [ ] `wiki/b-reference/gateway/features/trustee.md` Budget-Service-Sektion
+ aktualisieren
+- [ ] Dieses Dokument -> `z-archive/` verschoben
diff --git a/c-work/1-plan/2026-04-trustee-workflow-audit-and-run-workspace.md b/c-work/1-plan/2026-04-trustee-workflow-audit-and-run-workspace.md
new file mode 100644
index 0000000..8fbeb62
--- /dev/null
+++ b/c-work/1-plan/2026-04-trustee-workflow-audit-and-run-workspace.md
@@ -0,0 +1,206 @@
+
+
+
+
+# Trustee Workflow-Audit (A1) & Generischer Workflow-Run-Workspace (A2)
+
+## Beschreibung und Kontext
+
+Der User klickt im Trustee-Dashboard Service-Karten an (Budget-Vergleich,
+KPI-Dashboard, Cashflow, Forecast, Jahresabschluss-Pruefung) und landet auf
+Tabs in `TrusteeAnalyseView` / `TrusteeAbschlussView`, wo der jeweilige
+Workflow gestartet wird.
+
+Zwei Themen:
+
+- **A1 Audit:** Sind diese Workflows wirklich auf dem aktuellen
+ Pick-not-Push / Typed-Action-Stack? -- **JA, alle GREEN** (siehe Befund
+ unten). Hier wird nur dokumentiert.
+- **A2 Result-Sichtbarkeit:** Wenn der User waehrend des Runs die Seite
+ wechselt, ist das Resultat (KI-Antwort + generierte Files) anschliessend
+ weg -- es lebt nur im React-State der Source-View. Persistenz existiert
+ zwar in `AutoRun`/`AutoStepLog`, ist aber nirgends als User-UI
+ erschlossen.
+
+**Geschaeftstreiber:** UX-Bruch ("ich war kurz auf einer anderen Seite, jetzt
+ist mein Report weg") + fehlende Single-Source-of-Truth fuer Workflow-
+Outputs. Plus: User-Anforderung, dass jeder Workflow eine
+`FeatureInstance` referenziert (auch bei Scheduled-Runs), damit Datenquellen
+im Editor sauber gefiltert werden koennen.
+
+## Fokus und kritische Details
+
+- `POST /api/workflows/{instanceId}/execute` ist heute **synchron** -- der
+ Browser blockiert auf dem Request bis Ende. Der "Workspace" muss diesen
+ Request nicht aendern, er muss nur die persistierten `AutoRun`-Daten
+ verlinkbar/auffindbar machen.
+- `Workflow.featureInstanceId` ist heute optional (zumindest historisch).
+ Pflicht-Binding ist breakend fuer existierende Workflows ohne Instanz --
+ Migration noetig.
+- Scheduler-Pfad muss die Pflicht-Binding **respektieren**, sonst laufen
+ Cron-Workflows ohne Daten-Scope.
+- Tab-Position: User wuenscht den Workspace explizit unter
+ `/automations` (Seite "Nutzung > Automation") als zusaetzlichen Tab neben
+ Dashboard + Workflows.
+
+## Ziel und Nicht-Ziele
+
+- Ziel A1: Audit-Befund GREEN dokumentiert (Wiki-Update).
+- Ziel A2: Generische Workflow-Run-Workspace-View; jeder Workflow hat
+ Pflicht-FeatureInstance-Binding; Trustee-Views zeigen Resultate ueber
+ Workspace statt eigenem React-State.
+- NICHT: Async-Umstellung des Execute-Endpoints (eigener Plan, falls noetig).
+- NICHT: Browser-Push-Notification (User wollte nur Workspace).
+- NICHT: Aenderung der Workflow-Engine-Logik selbst.
+
+## Betroffene Module
+
+- Gateway:
+ - `gateway/modules/features/graphicalEditor/datamodelFeatureGraphicalEditor.py`
+ (`Workflow.featureInstanceId` Pflicht).
+ - `gateway/modules/features/graphicalEditor/routeFeatureGraphicalEditor.py`
+ (Save-Validation, neue Aggregat-Routen unter `/api/automations/runs/...`).
+ - `gateway/modules/features/graphicalEditor/mainScheduler.py` (Schedule-
+ Erstellung uebernimmt Instanz aus Workflow).
+ - `gateway/modules/features/trustee/mainTrustee.py` (Audit-Notiz,
+ sicherstellen dass Templates `featureInstanceId` setzen).
+- Frontend:
+ - Neuer Tab in `frontend_nyla/src/pages/AutomationsDashboardPage.tsx`.
+ - Neue Komponenten `WorkflowRunWorkspaceView`,
+ `WorkflowRunDetailView`.
+ - FlowEditor-Toolbar bekommt Pflicht-Selector "Feature-Instanz".
+ - `frontend_nyla/src/pages/views/trustee/TrusteeAnalyseView.tsx` und
+ `TrusteeAbschlussView.tsx` schlanker (Verlinkung in den Workspace
+ statt eigene Result-Anzeige; Run-State nicht mehr verloren bei Tab-
+ Wechsel weil Persistenz uebernimmt).
+- DB-Migration: ja -- Bestand-Workflows ohne `featureInstanceId` brauchen
+ ein Migrations-Skript (interaktiv).
+- RBAC: Zugriff auf `/automations/workspace` -- prueft pro Run die
+ Mandate/FeatureInstance-Rechte des Users.
+
+## Befund A1 (Audit) -- bereits GREEN
+
+| Service | Workflow-ID | Backend-Definition | Status | Beleg |
+|---------|-------------|---------------------|--------|-------|
+| Budget-Vergleich | `trustee-budget-comparison` | `mainTrustee.py` 430-461 | GREEN | DataRef Trigger-Payload + Refresh-Output, modern |
+| KPI-Dashboard | `trustee-kpi-dashboard` | `mainTrustee.py` 463-478 | GREEN | `_buildAnalysisWorkflowGraph` 364-381 |
+| Cashflow-Rechnung | `trustee-cashflow` | `mainTrustee.py` 480-492 | GREEN | dito |
+| Prognose | `trustee-forecast` | `mainTrustee.py` 494-507 | GREEN | dito |
+| Jahresabschluss-Pruefung | `trustee-year-end-check` | `mainTrustee.py` 509-522 | GREEN | dito |
+
+Engine-Pipeline: `executeGraph` ruft `materializeFeatureInstanceRefs` +
+`validateGraph` vor jedem Lauf auf
+(`gateway/modules/features/graphicalEditor/executionEngine.py` 341-350).
+Persistierte Graphs nutzen zunaechst nackte UUID fuer `featureInstanceId`
+(Bootstrap `interfaceFeatures.py` 336-338), Laufzeit-Envelope erfolgt in
+`executeGraph`.
+
+## Entscheidungen
+
+| Datum | Entscheidung | Begruendung |
+|-------|-------------|------------|
+| 2026-04-29 | Workspace ist GENERISCH plattformweit, nicht Trustee-spezifisch | Doppelt-Bauen vermeiden; ComCoach/TeamsBot/Workspace profitieren auch |
+| 2026-04-29 | Workspace lebt unter `/automations` als Tab "Workspace" | User-Vorgabe; Nutzungspfad "Nutzung > Automation > Workspace" |
+| 2026-04-29 | Workflow-Speicherung verlangt `featureInstanceId` | Datenquellen-Tools im Editor sind nur dann sinnvoll filterbar |
+| 2026-04-29 | Bestehende Workflows ohne Instanz: interaktives Migrations-Skript pro Mandate | Kein automatischer Default -- der User soll bewusst zuordnen |
+| 2026-04-29 | Browser-Push-Notification NICHT umgesetzt | User hat nur Workspace gewaehlt; Toast bei Run-Ende reicht |
+
+## Umsetzungs-Checkliste
+
+### Phase 1 -- FeatureInstance-Pflicht-Binding
+
+- [ ] Backend: `Workflow.featureInstanceId NOT NULL` (Pydantic +
+ DB-Constraint via Connector-Auto-Init).
+- [ ] Save-Validation in `routeFeatureGraphicalEditor.py`: Workflow ohne
+ `featureInstanceId` -> 400 mit Error-Detail "Feature-Instanz fehlt".
+ Save-with-errors (AC-9 vom Typed-Action-Plan) bleibt erlaubt -- nur
+ Run-Start blockt.
+- [ ] Run-Start (`POST .../execute`): Vorab-Check, dass Workflow eine
+ Instanz hat.
+- [ ] Migrations-Skript
+ `gateway/scripts/script_db_migrate_workflow_feature_instance.py`:
+ listet pro Mandate alle Workflows ohne Instanz, bietet pro Workflow
+ eine Auswahl (CLI prompt).
+- [ ] Scheduler: Schedule-Erstellung uebernimmt Instanz aus Workflow,
+ Override nicht moeglich.
+- [ ] FlowEditor-Toolbar: Pflicht-Dropdown "Feature-Instanz" oben links.
+ Datenquellen-Tools (z.B. UDB-Listen, FileItems, SourcesTab) filtern
+ automatisch auf diese Instanz.
+- [ ] Trustee-Templates pruefen: alle Workflows haben in
+ `mainTrustee.py` bereits `featureInstanceId` als
+ `{{featureInstanceId}}`-Placeholder, der beim
+ `_copyTemplateWorkflows` ersetzt wird (`interfaceFeatures.py`
+ 269-338) -- nur sicherstellen dass jetzt nichts haengen bleibt.
+
+### Phase 2 -- Generischer WorkflowRunWorkspace
+
+- [ ] Neue Aggregat-API:
+ `GET /api/automations/runs?scope=mine|mandate|all&status=...&limit=...`
+ und `GET /api/automations/runs/{runId}/detail`. Detail-Payload
+ kombiniert AutoRun + AutoStepLog + Outputs + verlinkte FileItems
+ (joined mit RBAC-Filter).
+- [ ] Tab "Workspace" in
+ `frontend_nyla/src/pages/AutomationsDashboardPage.tsx` (neben
+ Dashboard, Workflows).
+- [ ] Komponente `WorkflowRunWorkspaceView`: Liste mit Filter
+ (Status, Workflow-Template, Mandate, Zeitraum), 50-er Pagination.
+- [ ] Komponente `WorkflowRunDetailView`: Chat-aehnliche Ansicht
+ - Header: Workflow-Name, Status, Start/Ende, FeatureInstance.
+ - Eingabe-Bubble: Trigger-Payload (formatiert).
+ - Step-Bubbles: chronologisch, pro Step-Output kollabierbar.
+ - Final-Bubble: KI-Antwort als Markdown.
+ - Documents-Sektion: alle generierten FileItems als Karten mit
+ Direkt-Download (`a href="/api/files/{id}/download"`).
+- [ ] Trustee-Views umbauen:
+ - `TrusteeAnalyseView`: Result-Anzeige raus (`resultText` /
+ `resultDocuments` State), stattdessen "Im Workspace ansehen"-
+ Button mit `runId`.
+ - `TrusteeAbschlussView`: dasselbe (heute zeigt der gar nichts).
+
+### Phase 3 -- Notifications
+
+- [ ] Toast bei Run-Ende ist heute schon da -- erweitern um Klick-Action
+ zum Detail-View des Runs.
+- [ ] Sidebar-Badge auf Eintrag "Automation" (Counter "neu seit letztem
+ Besuch", `localStorage`-basiert).
+
+## Akzeptanzkriterien
+
+| # | Kriterium (Given-When-Then) | Prio |
+|---|-----------------------------|------|
+| 1 | Given Workflow ohne Instanz, When User Save klickt, Then 400 mit klarer Error-Message | must |
+| 2 | Given Bestand-Workflow ohne Instanz, When Migrations-Skript laeuft, Then User waehlt interaktiv pro Workflow eine Instanz und das Feld wird gesetzt | must |
+| 3 | Given Trustee-Run gestartet, When User waehrend Lauf die Seite wechselt und zurueck zum Tab "Workspace" geht, Then Run mit allen Outputs sichtbar | must |
+| 4 | Given Run mit generiertem File, When User auf Document-Karte klickt, Then File wird direkt heruntergeladen | must |
+| 5 | Given Run-Ende, When Toast erscheint, Then Klick fuehrt direkt zum WorkflowRunDetailView | should |
+| 6 | Given neue Runs seit letztem Besuch, When User die Sidebar sieht, Then "Automation" hat einen Counter-Badge | should |
+| 7 | Given FlowEditor offen ohne Instanz-Auswahl, When User Datenquellen-Tool oeffnen will, Then Hinweis "Bitte Feature-Instanz waehlen" | should |
+
+## Testplan
+
+| ID | AC | Art | Automatisiert | Repo-Pfad | Status |
+|----|----|-----|--------------|-----------|--------|
+| T1 | 1 | api | ja | gateway/tests/features/graphicalEditor/test_workflow_save_requires_instance.py | pending |
+| T2 | 2 | manual | nein | gateway/scripts/script_db_migrate_workflow_feature_instance.py | pending |
+| T3 | 3 | e2e | ja | frontend_nyla/tests/e2e/workflow-run-workspace.spec.ts | pending |
+| T4 | 4 | e2e | ja | wie T3 | pending |
+| T5 | 5 | manual | nein | -- | pending |
+| T6 | 6 | unit | ja | frontend_nyla/src/pages/__tests__/AutomationsDashboardPage.test.tsx | pending |
+| T7 | 7 | unit | ja | frontend_nyla/src/components/flowEditor/__tests__/Toolbar.test.tsx | pending |
+
+## Links
+
+- Audit-Quelle: Subagent-Report 2026-04-29.
+- Wiki: `wiki/b-reference/gateway/workflow.md`,
+ `wiki/b-reference/gateway/features/trustee.md`,
+ `wiki/c-work/4-done/2026-04-typed-action-architecture.md`,
+ `wiki/c-work/4-done/2026-04-automation-central-admin.md`.
+
+## Abschluss
+
+- [ ] `wiki/b-reference/gateway/workflow.md` Abschnitt
+ "Workflow-Run-Workspace" anlegen
+- [ ] `wiki/b-reference/gateway/features/trustee.md` Result-UX-Sektion
+ aktualisieren
+- [ ] `wiki/TOPICS.md` ggf. Tab-Beschreibung
+- [ ] Dieses Dokument -> `z-archive/` verschoben
diff --git a/c-work/2-build/2026-04-id-unified-knowledge-indexing-rag-concept.md b/c-work/3-validate/2026-04-id-unified-knowledge-indexing-rag-concept.md
similarity index 100%
rename from c-work/2-build/2026-04-id-unified-knowledge-indexing-rag-concept.md
rename to c-work/3-validate/2026-04-id-unified-knowledge-indexing-rag-concept.md
diff --git a/c-work/4-done/2026-04-bootstrap-migrations-cleanup.md b/c-work/4-done/2026-04-bootstrap-migrations-cleanup.md
new file mode 100644
index 0000000..6647b06
--- /dev/null
+++ b/c-work/4-done/2026-04-bootstrap-migrations-cleanup.md
@@ -0,0 +1,126 @@
+
+
+
+
+
+
+
+# Bootstrap-Migrations-Cleanup (idempotente Boot-Routinen)
+
+## Beschreibung und Kontext
+
+Beim App-Start liefen 5 idempotente "Self-Heal"- und "Backfill"-Routinen mit, die
+historisch noetig waren, um produktive DBs nachtraeglich auf neue Schemata zu
+heben (Mandate-Label-Logik, Slug-Regeln, Legacy-Root-Mandate, sysadmin-Split,
+RAG-Bytes-Aggregation). Auf modernen DBs sind sie No-Ops und haben pro Boot
+Tabellen-Scans gekostet plus den Lifespan-Pfad unleserlich gemacht.
+
+**Resultat:** alle 5 Routinen aus dem Boot-Pfad entfernt, Tests fuer entfernte
+Routinen geloescht, ein leichter Telemetrie-Helper (`runLegacyDataChecks`)
+warnt im Boot-Log falls je doch noch Restbestand auftauchen sollte.
+
+## Was wurde gemacht (chronologisch)
+
+1. **Verifikation pro Kandidat** -- Code-Pfade in
+ `gateway/modules/interfaces/interfaceBootstrap.py` und
+ `gateway/modules/interfaces/interfaceDbKnowledge.py` neu vermessen.
+ Ergebnis: Plan-Annahmen waren teils ungenau (Pfad `system/` statt
+ `interfaces/`, ganze Funktionen statt einzelner Bloecke); Plan-Tabelle in
+ `1-plan/` korrigiert vor Removal.
+2. **Audit-Skript** `gateway/scripts/script_db_audit_legacy_state.py` (NEU)
+ - Lese-only fuer 4 Mandate-/Role-Checks (App-DB) + 1 RAG-Check (Knowledge-DB).
+ - Optional `--purge-rag-orphans` Flag fuer den einzigen Daten-Cleanup.
+ - Exit-Code 0=GREEN / 1=RED / 2=ERROR fuer CI-Gating.
+ - JSON-Output-Modus.
+3. **Audit-Lauf gegen Dev-DB:**
+ - 4 Checks GREEN sofort.
+ - 1 Check RED: 20 `FileContentIndex`-Rows ohne `mandateId`+`featureInstanceId`
+ (alte Test-Datei-Reste).
+4. **Purge der 20 RAG-Orphans** via `--purge-rag-orphans`. Re-Audit: 5/5 GREEN.
+5. **Telemetrie-Helper** `gateway/modules/interfaces/_legacyMigrationTelemetry.py`
+ (NEU) -- 4 nicht-blockierende SELECT-Checks (gleiche Logik wie Audit-Skript),
+ prozessweit gecached, Aufruf am Ende von `initBootstrap`. WARN-Log bei Restbestand.
+6. **Code-Removals** in `gateway/modules/interfaces/interfaceBootstrap.py`:
+ - `_migrateMandateDescriptionToLabel` (Funktion + Aufruf) -- weg.
+ - `_migrateMandateNameLabelSlugRules` (Funktion + Aufruf) -- weg.
+ - `initRootMandate` Legacy-Block (`recordFilter={"name":"Root"}` + recordModify) -- weg.
+ Funktion `initRootMandate` selbst bleibt (legt Root-Mandat an wenn fehlt).
+ - `_migrateAndDropSysAdminRole` (Funktion + Aufruf) -- weg.
+ - `aggregateMandateRagTotalBytes` Fallback `try`/`except`-Block (Z. 606-635) --
+ weg. Funktion bleibt aktiv (4 Caller).
+7. **Test-Cleanup** -- folgende Test-Files geloescht (referenzierten entfernte
+ Funktionen, wuerden ImportError werfen):
+ - `gateway/tests/unit/bootstrap/test_mandateNameMigration.py` (8 Tests)
+ - `gateway/tests/unit/rbac/test_sysadmin_migration.py` (5 Tests)
+ - Verzeichnis `gateway/tests/unit/bootstrap/` ist jetzt leer und entfernt.
+8. **Smoke-Tests:** `python -m pytest tests/unit/rbac` -- 17/17 GREEN.
+ Import-Smoke fuer alle 3 geaenderten Module GREEN.
+9. **Telemetrie-Aufruf** in `initBootstrap` als letzten Schritt nach
+ `_bootstrapBilling()`. Try/Except umschlossen damit Telemetrie nie
+ den Boot crashed.
+
+## Geaenderte Dateien
+
+| Datei | Aenderung |
+|-------|----------|
+| `gateway/modules/interfaces/interfaceBootstrap.py` | -3 Funktionen (~120 Zeilen), -3 Aufrufe, +6 Zeilen Telemetrie-Hook |
+| `gateway/modules/interfaces/interfaceDbKnowledge.py` | -27 Zeilen Fallback-Block, vereinfachtes Log-Format |
+| `gateway/modules/interfaces/_legacyMigrationTelemetry.py` | NEU (~145 Zeilen) |
+| `gateway/scripts/script_db_audit_legacy_state.py` | NEU (~290 Zeilen) |
+| `gateway/tests/unit/bootstrap/test_mandateNameMigration.py` | GELOESCHT |
+| `gateway/tests/unit/rbac/test_sysadmin_migration.py` | GELOESCHT |
+
+## Audit-Befund (Dev-DB, 2026-04-29 19:50)
+
+| # | Check | Ergebnis nach Purge |
+|---|-------|---------------------|
+| 1 | mandate-description-to-label | GREEN (0) |
+| 2 | mandate-name-slug-rules | GREEN (0) |
+| 3 | root-mandate-legacy | GREEN (0) |
+| 4 | sysadmin-role | GREEN (0) |
+| 5 | rag-fallback-orphan-index | GREEN (0, nach Purge von 20 Test-Resten) |
+
+## Operative Hinweise fuer Int + Prod
+
+User-Statement (2026-04-29): "die codebase lief bereits auf int und main/prod" --
+d.h. die idempotenten Migrations sind dort schon mehrfach durchgelaufen, das
+Removal-Risiko = 0. Trotzdem empfohlen vor dem Deploy:
+
+```pwsh
+# (1) Audit lesend laufen lassen -- ohne Aenderung
+python -m scripts.script_db_audit_legacy_state
+
+# (2) Falls Check 5 RED (RAG-Orphans): purgen
+python -m scripts.script_db_audit_legacy_state --purge-rag-orphans
+```
+
+Falls trotzdem Restbestand auftauchen sollte (alter DB-Restore o.ae.), feuert
+nach dem Boot der Telemetrie-Helper eine WARN-Logzeile pro betroffenem Check
+mit Routine-Name + IDs. Dann manuell mit dem Audit-Skript untersuchen.
+
+## Folgearbeiten (separate Plans)
+
+- **Scripts-Inventar:** subagent hat 27 Files in `gateway/scripts/` kategorisiert.
+ Klar Archiv-Kandidaten (B): `check_orphan_featureinstance.py` (hardcoded UUIDs),
+ `script_db_cleanup_duplicate_roles.py` (IS-NULL-Bug-Fix; Bug laengst gefixt).
+ Unklar (C): `_listMandates.py`, `migrate_async_to_sync.py`,
+ `i18n_rekey_plaintext_keys.py`, `script_db_migrate_accessrules_objectkeys.py`.
+ Restliche 21 Files = AKTIV. Entscheidung pro File offen.
+- **Telemetrie-Lebenszyklus:** in 30+ Tagen pruefen ob Telemetrie-Helper
+ Treffer hatte. Wenn 0, Helper komplett entfernen.
+
+## Akzeptanzkriterien
+
+| # | Kriterium | Status |
+|---|-----------|--------|
+| 1 | Audit-Skript laeuft Lese-only und liefert Exit-Code 0 wenn alle Checks GREEN | erfuellt |
+| 2 | Boot mit entfernten Routinen startet ohne Fehler, Telemetrie-Hook laeuft am Ende | erfuellt (Smoke-Test gruen) |
+| 3 | Telemetrie-Helper feuert klare WARN-Zeile bei Restbestand | erfuellt (Code-Pfad da, nicht ausgeloest da Dev GREEN) |
+| 4 | Tests fuer entfernte Funktionen sind weg, restliche Tests laufen | erfuellt (17/17 rbac+bootstrap green) |
+
+## Links
+
+- Audit-Skript: `gateway/scripts/script_db_audit_legacy_state.py`
+- Telemetrie: `gateway/modules/interfaces/_legacyMigrationTelemetry.py`
+- Verwandte Done-Plans: `wiki/c-work/4-done/2026-04-mandate-name-label-logic.md`,
+ `wiki/c-work/4-done/2026-04-sysadmin-authority-split.md`.
diff --git a/c-work/2-build/2026-04-infomaniak-connector.md b/c-work/4-done/2026-04-infomaniak-connector.md
similarity index 100%
rename from c-work/2-build/2026-04-infomaniak-connector.md
rename to c-work/4-done/2026-04-infomaniak-connector.md
diff --git a/c-work/2-build/2026-04-msft-google-calendar-contacts.md b/c-work/4-done/2026-04-msft-google-calendar-contacts.md
similarity index 100%
rename from c-work/2-build/2026-04-msft-google-calendar-contacts.md
rename to c-work/4-done/2026-04-msft-google-calendar-contacts.md
diff --git a/c-work/_CHANGELOG.md b/c-work/_CHANGELOG.md
index 55c69eb..fc967f7 100644
--- a/c-work/_CHANGELOG.md
+++ b/c-work/_CHANGELOG.md
@@ -14,6 +14,11 @@ Skip: reine Refactors, Formatting, Lint, Dep-Bumps, Test-only, Wiki-Tippfehler.
## 2026-04-29
+- 2026-04-29 | refactor | gateway | **Bootstrap-Cleanup ausgefuehrt: 4 idempotente Migrations-Routinen + 1 Aggregations-Fallback aus dem Boot-Pfad entfernt.** Vor Removal Audit-Skript `gateway/scripts/script_db_audit_legacy_state.py` (NEU, lese-only, exit-code-gated) gegen Dev-DB gelaufen -> 4/5 GREEN sofort, Check 5 (RAG-Orphans) RED mit 20 verwaisten `FileContentIndex`-Rows ohne `mandateId`/`featureInstanceId` -> via `--purge-rag-orphans` bereinigt -> Re-Audit 5/5 GREEN. Dann entfernt aus `gateway/modules/interfaces/interfaceBootstrap.py`: `_migrateMandateDescriptionToLabel` (Funktion + Aufruf), `_migrateMandateNameLabelSlugRules` (Funktion + Aufruf, ~64 Zeilen), `initRootMandate`-Legacy-Block (`name="Root"`-Migration, 7 Zeilen; Funktion selbst bleibt), `_migrateAndDropSysAdminRole` (Funktion + Aufruf, ~95 Zeilen). In `interfaceDbKnowledge.py`: `aggregateMandateRagTotalBytes`-Fallback-Block (`try`/`except` mit FileItem-ID-Korrelation aus Management-DB, ~27 Zeilen) entfernt -- die Funktion bleibt aktiv, da sie 4 externe Caller hat. Ersatz: neuer Helper `gateway/modules/interfaces/_legacyMigrationTelemetry.py` mit 4 lese-only WARN-Checks (gleiche Logik wie Audit-Skript, prozessweit gecached) wird am Ende von `initBootstrap` einmalig aufgerufen -- falls je doch Restbestand auftauchen sollte (alter DB-Restore o.ae.), gibt's klare WARN-Logs mit Routine-Name + IDs. Tests `tests/unit/bootstrap/test_mandateNameMigration.py` (8 Tests) und `tests/unit/rbac/test_sysadmin_migration.py` (5 Tests) geloescht (referenzierten entfernte Funktionen, wuerden ImportError werfen). Smoke-Test: 17/17 verbliebene rbac+bootstrap-Tests GREEN, Imports aller drei Module GREEN. User-Statement zur Risk-Lage: "die codebase lief bereits auf int und main/prod" -- d.h. die idempotenten Migrations sind dort schon mehrfach durchgelaufen, das Removal-Risiko = 0; das Audit-Skript bleibt fuer pre-deploy-Gating. Plan urspruenglich in `1-plan/`, jetzt direkt in `4-done/2026-04-bootstrap-migrations-cleanup.md`. (c-work: `4-done/2026-04-bootstrap-migrations-cleanup.md`)
+
+- 2026-04-29 | feat | frontend-nyla, gateway | **Generischer `frontendType: templateTextarea`** fuer Freitext mit `{{nodeId.path}}`-Variablen (DataPicker-Insert); `email.draftEmail.context` und `ai.prompt.aiPrompt` nutzen ihn statt reiner Textarea -- Aufloesung ausschliesslich via bestehendes `resolveParameterReferences` (kein Loop-Spezialcode im Executor). Loop-Preview-Enrichment + IfElse/AI-`responseData`-Picker bleiben. Unit-Test `test_legacy_string_template_loop_current_item_nested` in `test_automation2_graphUtils.py`.
+- 2026-04-29 | docs | wiki | **6 neue UX-/Architektur-Plaene angelegt** in `wiki/c-work/1-plan/` aus Topics-TODO A-F: (A1+A2) Trustee-Workflow-Audit (alle 5 Reporting-Workflows GREEN auf Pick-not-Push) + generischer `WorkflowRunWorkspace`-Tab unter `/automations` als Single-Source-of-Truth fuer Workflow-Resultate, plus Pflicht-Binding `Workflow.featureInstanceId` (`2026-04-trustee-workflow-audit-and-run-workspace.md`); (B) Budget-Vergleich auf Excel-only mit eindeutigem 1-Tabelle-1-Chart-Prompt, Word-Pfad raus (`2026-04-trustee-budget-comparison-refactor.md`); (C) Inventar idempotenter Boot-Routinen, Removal-Plan fuer 5 b-Kandidaten via Audit-Skript + 30-Tage-Telemetrie (`2026-04-bootstrap-migrations-cleanup.md` -- noch am gleichen Tag umgesetzt, siehe Eintrag oben, jetzt unter `4-done/`); (D) Theme-System fuer AI-Reports pro Workflow (4 Themes standard/finance/marketing/presentation + Mandate-CI-Overlay), MD->JSON-Konsolidierung, Renderer-Refactor, Bilder-in-Tabellenzellen (`2026-04-ai-reports-theming-and-pipeline.md`); (E) ComCoach Greenfield-IA mit `TrainingModule`-Datenmodell (Rename `CoachingContext`) + 5-Tab-Struktur Dashboard/Assistent/Module/Session/Einstellungen, dossier=coaching-Doppelung weg, `?context=`-Bug fix (`2026-04-comcoach-greenfield-ia.md`); (F) TeamsBot Greenfield-IA analog mit neuer `MeetingModule`-Entity + 5-Tab-Struktur und 4 Live-Update-Fixes (SSE-Reconnect-Hook, agentRun-Status-Bubble, stats-Karten, adaptives Dashboard-Polling) (`2026-04-teamsbot-greenfield-ia-and-live-update.md`). Empfohlene Reihenfolge der Bauphase: C -> A2-Phase-1 (FeatureInstance-Pflicht) -> A2-Phase-2/3 (Workspace) -> B -> D -> E/F parallel. (c-work: 6 neue Dateien in `1-plan/`)
+
- 2026-04-29 | fix | gateway | **`checkForDuplicateFile` macht jetzt einen RBAC-Cross-Check und liefert keine "Geister-Duplicate" mehr aus.** Ursache des `File with ID ... not found`-Fehlers in `downloadFromDataSource` direkt nach `Duplicate detected for user ...`: `interfaceDbComponent` wird ueber `serviceHub` ohne `featureInstanceId` initialisiert, `checkForDuplicateFile` faellt deshalb auf den `mandateId`-Filter zurueck und benutzte `db.getRecordset` (kein RBAC) -- damit konnte er ein File aus einer FREMDEN featureInstance returnen. Der nachfolgende `updateFile`-Aufruf prallte dann am RBAC-gefilterten `getFile` ab und crashte den ganzen Tool-Call. Sauberer Fix an der Wurzel: nach dem Recordset-Treffer zwingend `self.getFile(fileId)` als RBAC-Cross-Check; wenn `None`, gilt als kein Duplicate fuer den aktuellen Scope und der Caller (`saveUploadedFile`/`createFile`) erstellt eine frische per-Scope-Kopie. Damit entfaellt jeglicher Workaround in `updateFile` (die in der Vorgaenger-Session gebaute Try/Except-Schleife wurde gestern bereits ausgebaut, weil sie in einer Sackgasse stand). Folge: identische Files koennen pro Mandate mehrfach existieren -- eines pro featureInstance -- was gewollt ist, da sie pro Scope eigene Folder-/Tag-/Neutralize-Metadaten brauchen. Symptom-Doku in `wiki/b-reference/gateway/agent-file-bridge.md` (Section "Ghost-Duplicate-Fix"). (c-work: Begleitfix zum Agent-File-Bridge-Build vom selben Tag)
- 2026-04-29 | fix | gateway | **Agent-Tools binden jetzt jede produzierte Datei als ChatDocument an den aktiven Workflow -- damit funktioniert der `documentList`-Resolver fuer Agent-Outputs.** Bisher erzeugten `downloadFromDataSource`, `writeFile mode=create`, `renderDocument`, `generateImage` und `createChart` zwar eine `FileItem` (via `saveUploadedFile`/`saveGeneratedFile`), liessen den Workflow-Document-Graph aber unberuehrt -- statt eines `ChatDocument` floss nur ein `sideEvent fileCreated` an die UI. Folge: `getChatDocumentsFromDocumentList` (und damit `ai_summarizeDocument` / `ai_process` / `context_extractContent` / `context_neutralizeData`) konnte die FileItem-IDs nicht aufloesen, weil der Resolver ausschliesslich `workflow.messages[*].documents[*].id` matcht. Symptom war "Building structure prompt with 0 valid ContentParts" und entsprechend leere Summaries direkt nach einem Agent-Download. Loesung: zentraler Helper `_attachFileAsChatDocument(services, fileItem, label, userMessage)` in `coreTools/_helpers.py`, der intern eine ChatMessage (Rolle `assistant`, Status `step`, generierter `documentsLabel`) inklusive einem ChatDocument (mit `roundNumber`/`taskNumber`/`actionNumber` aus dem aktiven Workflow) via `chatService.storeMessageWithDocuments` persistiert -- exakt das Pattern, das `workflowProcessor.persistTaskResult` und `methodTrustee.extractFromFiles` schon laenger benutzen. Helper wird jetzt aus allen fuenf File-erzeugenden Agent-Tools aufgerufen; der ToolResult-Text traegt einheitlich beide IDs (`documentList ref: docItem:` fuer AI-Tools, `file id: ` fuer `readFile`/Embeds). Tool-Descriptions + `conversationManager.buildSystemPrompt` ergaenzt um die explizite Anweisung "use docItem: in documentList, NOT the file id, NOT a `{"documents":[...]}` wrapper". Zusatzlich: `mainServiceAgent.runAgent` propagiert das Workflow-Object jetzt in alle Service-Contexts (`chat._context.workflow` etc.) -- bisher tat das nur `workflowManager._propagateWorkflowToContext`, weshalb der Agent ueber die Workspace-Route ohne aktiven Workflow-Context lief und `chatService._workflow` `None` war. Folge: der Helper konnte ohne diesen Propagations-Fix nie greifen. Die in der vorhergehenden Session eingebaute RBAC-tolerante `updateFile`-Try/Except-Schleife in `_downloadFromDataSource` ist jetzt obsolet und wieder ausgebaut -- mit korrektem ChatDocument-Bind kommt der `featureInstanceId` ueber den Workflow-Pfad und die Duplicate-File-RBAC-Probleme verschwinden an der Wurzel. Pattern-Doc neu unter `wiki/b-reference/gateway/agent-file-bridge.md`. (c-work: kein dedizierter Eintrag, kritischer Fix on top der documentList-Coercer-Aenderung)
| |