From cb18a584a2f22b1a26e8777260b7751ca395b0e1 Mon Sep 17 00:00:00 2001
From: ValueOn AG
Date: Sat, 11 Apr 2026 19:45:08 +0200
Subject: [PATCH] fixed language logic items
---
...customer-trustee-tooling-and-demo-prep.md} | 0
.../2026-04-i18n-static-text-elimination.md | 4 -
...6-04-gateway-int-stability-and-bugfixes.md | 64 +--
.../2026-04-i18n-static-text-elimination.md | 377 +-----------------
d-guides/coding-conventions.md | 51 +++
5 files changed, 90 insertions(+), 406 deletions(-)
rename c-work/1-plan/{2026-04-trustee-tooling-and-demo-prep.md => 2026-04-customer-trustee-tooling-and-demo-prep.md} (100%)
delete mode 100644 c-work/1-plan/2026-04-i18n-static-text-elimination.md
rename c-work/{1-plan => 4-done}/2026-04-gateway-int-stability-and-bugfixes.md (62%)
diff --git a/c-work/1-plan/2026-04-trustee-tooling-and-demo-prep.md b/c-work/1-plan/2026-04-customer-trustee-tooling-and-demo-prep.md
similarity index 100%
rename from c-work/1-plan/2026-04-trustee-tooling-and-demo-prep.md
rename to c-work/1-plan/2026-04-customer-trustee-tooling-and-demo-prep.md
diff --git a/c-work/1-plan/2026-04-i18n-static-text-elimination.md b/c-work/1-plan/2026-04-i18n-static-text-elimination.md
deleted file mode 100644
index 9b69965..0000000
--- a/c-work/1-plan/2026-04-i18n-static-text-elimination.md
+++ /dev/null
@@ -1,4 +0,0 @@
-
-
-
-Dieser Plan wurde nach **`c-work/2-build/2026-04-i18n-static-text-elimination.md`** verschoben. Bitte dort weiterarbeiten.
diff --git a/c-work/1-plan/2026-04-gateway-int-stability-and-bugfixes.md b/c-work/4-done/2026-04-gateway-int-stability-and-bugfixes.md
similarity index 62%
rename from c-work/1-plan/2026-04-gateway-int-stability-and-bugfixes.md
rename to c-work/4-done/2026-04-gateway-int-stability-and-bugfixes.md
index 417b4a0..58aba00 100644
--- a/c-work/1-plan/2026-04-gateway-int-stability-and-bugfixes.md
+++ b/c-work/4-done/2026-04-gateway-int-stability-and-bugfixes.md
@@ -1,5 +1,6 @@
-
+
+
# Gateway: INT-Stabilität — Analyse (Log) und Fix-Plan
@@ -8,7 +9,7 @@
Beim Testen der Applikation auf der **Integrations-Instanz (INT)** mit **mehreren parallelen Nutzern** wurden Symptome im Gateway-Log sichtbar (u. a. `gateway_log_2026-04-08T11-14-17-514Z.log`). Ziel dieses Dokuments ist eine **codebezogene Ursachenanalyse** und ein **Umsetzungs- und Testplan** — ohne die Themen mit Architektur-Storys zu vermischen.
-**Business-Treiber:** Zuverlässiger Multi-User-Betrieb (Upload/Index, Workspace-Agent, Outlook-Tools, Knowledge/Neutralisierung) und weniger „sporadische“ Fehler, die Support-Zeit kosten.
+**Business-Treiber:** Zuverlässiger Multi-User-Betrieb (Upload/Index, Workspace-Agent, Outlook-Tools, Knowledge/Neutralisierung) und weniger „sporadische" Fehler, die Support-Zeit kosten.
**Risiko bei Nicht-Umsetzung:** Weiterhin nicht reproduzierbar wirkende Permission-Fehler nach erfolgreicher Verarbeitung, fehlgeschlagene OpenAI-Streaming-Calls wegen Tool-Schema, nutzloser Outlook-Tool-Flow bei UUID-Referenzen, Crashes/Fail-Safe-Sprünge bei Bild-only-Indexierung.
@@ -21,35 +22,41 @@ Beim Testen der Applikation auf der **Integrations-Instanz (INT)** mit **mehrere
- **Fragil:** `interfaceDbManagement.getInterface()` liefert eine **gemeinsame** `ComponentObjects`-Instanz (`"default"`); jeder Aufruf setzt `setUserContext` auf demselben Objekt.
- **Edge Case:** Async-Pfade (z. B. `_autoIndexFile` mit `await knowledgeService.indexFile`) — zwischen Start und `updateFile` kann ein anderer Request den Kontext überschreiben → `PermissionError` auf `FileItem.update` trotz erfolgreicher Indexierung.
- **Code:** `gateway/modules/interfaces/interfaceDbManagement.py`, `gateway/modules/routes/routeDataFiles.py` (`_autoIndexFile`).
+- **Fix:** `_autoIndexFile` holt `mgmtInterface` nach dem letzten `await` frisch. Singleton bleibt bestehen — vollständige Ablösung ist separates Architektur-Thema.
### Thema B — Agent-Tool-JSON-Schema: Arrays ohne `items`
- **Fragil:** `ActionToolAdapter` mappt `List[str]` auf JSON-Schema `type: array` **ohne** `items`.
- **Edge Case:** OpenAI lehnt Tools mit HTTP 400 ab (*array schema missing items*); Failover zu anderen Providern maskiert das Problem.
- **Code:** `gateway/modules/serviceCenter/services/serviceAgent/actionToolAdapter.py`; betroffene Aktion u. a. `ai.process` (`methodAi.py`, Parameter `documentList`).
+- **Fix:** Neues `_ARRAY_ITEMS_MAPPING` ordnet Python-Array-Typen dem korrekten `items`-Schema zu. `_convertParameterSchema` setzt bei `type: "array"` automatisch `items` (Fallback: `{"type": "string"}`).
### Thema C — Outlook: Referenzformat vs. Lookup
- **Fragil:** `listConnections` zeigt u. a. `id: `; `getUserConnectionFromConnectionReference` parst nur `connection:{authority}:{username}`.
-- **Edge Case:** Modell übergibt UUID an Outlook-Tools → „Connection not found“.
+- **Edge Case:** Modell übergibt UUID an Outlook-Tools → „Connection not found".
- **Code:** `coreTools/_connectionTools.py`, `mainServiceChat.getUserConnectionFromConnectionReference`, `methodOutlook/helpers/connection.py`.
+- **Fix:** `listConnections` gibt jetzt `connection:{authority}:{username}` aus statt UUIDs. `getUserConnectionFromConnectionReference` hat UUID-Fallback: wenn Format nicht `connection:…`, wird nach Connection-ID gesucht.
### Thema D — Knowledge: `_neutralSvc` bei nur Bildern
- **Bug:** `_neutralSvc` wird nur im Zweig mit Textobjekten gesetzt; Bild-Neutralisierung nutzt `_neutralSvc` danach — bei **keinen** Textobjekten → `UnboundLocalError` (im Log als fail-safe sichtbar).
- **Code:** `gateway/modules/serviceCenter/services/serviceKnowledge/mainServiceKnowledge.py` (`indexFile`).
+- **Fix:** `_neutralSvc` wird vor Text- und Bild-Block initialisiert (`None`), sobald `_shouldNeutralize` wahr ist. Bild-Block prüft `_neutralSvc` in der Bedingung.
### Thema E — Workspace: Mandats-Kontext aus zwei Quellen
- **Fragil:** `_getChatInterface` nutzt `context.mandateId`; `ServiceCenterContext` für den Agent nutzt Mandat aus `_validateInstanceAccess` (Instanz).
-- **Edge Case:** Unterschiedliche `interfaceDbChat`-Cache-Keys für „denselben“ Workspace → seltene Inkonsistenzen (Workflow sichtbar / nicht sichtbar).
+- **Edge Case:** Unterschiedliche `interfaceDbChat`-Cache-Keys für „denselben" Workspace → seltene Inkonsistenzen (Workflow sichtbar / nicht sichtbar).
- **Code:** `gateway/modules/features/workspace/routeFeatureWorkspace.py`.
+- **Fix:** `_getChatInterface` akzeptiert optionalen `mandateId`-Parameter. **Alle 34** `_validateInstanceAccess`-Aufrufe entpacken den Return-Wert. Alle `_getChatInterface`- und `ServiceCenterContext`-Aufrufe nutzen die validierte Instanz-`mandateId`.
### Thema F — Chat: `createMessage` / `getWorkflow` → None
-- **Fragil:** `getWorkflow` liefert `None` bei leerem RBAC-Recordset **und** bei Exceptions beim Validieren des `ChatWorkflow`-Modells; `createMessage` meldet pauschal „No access to workflow“.
-- **Edge Case:** Agent-Lauf endet „complete“, Persistenz der Assistant-Message schlägt fehl — Ursache ohne besseres Logging schwer trennbar.
+- **Fragil:** `getWorkflow` liefert `None` bei leerem RBAC-Recordset **und** bei Exceptions beim Validieren des `ChatWorkflow`-Modells; `createMessage` meldet pauschal „No access to workflow".
+- **Edge Case:** Agent-Lauf endet „complete", Persistenz der Assistant-Message schlägt fehl — Ursache ohne besseres Logging schwer trennbar.
- **Code:** `gateway/modules/interfaces/interfaceDbChat.py`.
+- **Fix:** `getWorkflow` unterscheidet RBAC-leer (`debug`: "RBAC filter or not found") vs. Validierungsfehler (`error`: "data validation failed"). `createMessage` loggt `warning` mit Verweis auf Ursache.
## Ziel und Nicht-Ziele
@@ -68,36 +75,37 @@ Beim Testen der Applikation auf der **Integrations-Instanz (INT)** mit **mehrere
| Datum | Entscheidung | Begründung |
|-------|-------------|------------|
| 2026-04-08 | Plan-Dokument angelegt | INT-Log + Code-Review; Umsetzung folgt in `2-build`. |
+| 2026-04-11 | Themen A–F umgesetzt (pragmatische Fixes) | Kein Grossrefactor Singleton; stattdessen Re-Acquire nach `await` + konsistente `mandateId`-Übergabe. |
## Umsetzungs-Checkliste (Fix-Reihenfolge empfohlen)
-- [ ] **D** `_neutralSvc` / Bildpfad in `mainServiceKnowledge.indexFile` absichern (klein, klar).
-- [ ] **B** JSON-Schema für `List[str]` / `List[int]` (und ggf. weitere Array-Typen) in `actionToolAdapter` ergänzen.
-- [ ] **C** Connection-Lookup UUID **oder** einheitliche Ausgabe/Beschreibung in `listConnections` + Parser in `getUserConnectionFromConnectionReference`.
-- [ ] **A** Management-Interface: Request-scoped Instanz statt globalem Singleton **oder** dokumentierter alternativer Ansatz (kein geteilter mutable User-Kontext über `await`-Grenzen).
-- [ ] **E** Workspace: einheitliche `mandateId`-Quelle für Chat-Interface und `ServiceCenterContext` nach Instanz-Validierung.
-- [ ] **F** `getWorkflow` / `createMessage`: präziseres Logging bei Validierungsfehlern; ggf. Unterscheidung „nicht gefunden“ vs. „Daten ungültig“.
+- [x] **D** `_neutralSvc` / Bildpfad in `mainServiceKnowledge.indexFile` absichern — `_neutralSvc` wird vor Text- und Bild-Block initialisiert; Bild-Block prüft `_neutralSvc` in der Bedingung. Datei: `serviceKnowledge/mainServiceKnowledge.py`.
+- [x] **B** JSON-Schema für `List[str]` / `List[int]` in `actionToolAdapter` — neues `_ARRAY_ITEMS_MAPPING`; `_convertParameterSchema` setzt bei `type: "array"` automatisch `items`. Datei: `serviceAgent/actionToolAdapter.py`.
+- [x] **C** Connection-Lookup: `listConnections` gibt `connection:{authority}:{username}` aus statt UUIDs; `getUserConnectionFromConnectionReference` hat UUID-Fallback. Dateien: `coreTools/_connectionTools.py`, `mainServiceChat.py`.
+- [x] **A** Management-Interface: pragmatischer Fix — `_autoIndexFile` holt `mgmtInterface` nach dem letzten `await` frisch. Singleton bleibt; vollständige Ablösung ist separates Architektur-Thema. Datei: `routes/routeDataFiles.py`.
+- [x] **E** Workspace `mandateId`: `_getChatInterface` akzeptiert optionalen `mandateId`-Parameter; alle 34 `_validateInstanceAccess`-Aufrufe entpacken den Return-Wert; alle `_getChatInterface`- und `ServiceCenterContext`-Aufrufe nutzen die validierte Instanz-`mandateId`. Datei: `features/workspace/routeFeatureWorkspace.py`.
+- [x] **F** `getWorkflow` / `createMessage` Logging: `getWorkflow` unterscheidet RBAC-leer (`debug`) vs. Validierungsfehler (`error`); `createMessage` loggt `warning` mit Verweis auf Ursache. Datei: `interfaces/interfaceDbChat.py`.
Weitere Checkliste aus Template:
-- [ ] API-Endpunkte (nur falls Signatur/Kontrakt ändert)
-- [ ] DB-Schema / Migration — nein
-- [ ] Frontend-Komponenten — nein (optional)
-- [ ] RBAC / Permissions — prüfen nach Fix A/E
-- [ ] Neutralisierung betroffen? — ja (Thema D)
-- [ ] Navigation / Routing — nein
-- [ ] Billing-Impact? — nein (keine bewusste Änderung der Abrechnungslogik)
+- [x] API-Endpunkte — keine Signaturänderung; `_getChatInterface` hat neuen optionalen Parameter (intern)
+- [x] DB-Schema / Migration — nein (nicht nötig)
+- [x] Frontend-Komponenten — nein
+- [x] RBAC / Permissions — keine Änderung; Fix E stellt sicher, dass Cache-Keys konsistent sind
+- [x] Neutralisierung betroffen? — ja (Thema D, behoben)
+- [x] Navigation / Routing — nein
+- [x] Billing-Impact? — nein
## Akzeptanzkriterien
-| # | Kriterium (Given-When-Then) | Prio |
-|---|---------------------------|------|
-| AC1 | Given eine Datei mit nur Bild-`contentObjects` und aktiver Neutralisierung, When `indexFile` läuft, Then kein `UnboundLocalError` und definiertes Verhalten (OK/skip mit Log). | must |
-| AC2 | Given generiertes Tool-Schema für `ai_process`, When es an die OpenAI-kompatible API geht, Then `documentList` ist ein `array` **mit** `items` (kein 400 wegen fehlender `items`). | must |
-| AC3 | Given `listConnections` liefert eine Verbindungs-UUID, When ein Outlook-Tool dieselbe Referenz nutzt, Then Verbindung wird aufgelöst **oder** die Tool-Konvention verbietet UUID explizit und der Agent folgt zuverlässig der Konvention. | must |
-| AC4 | Given zwei parallele Uploads/Auto-Index-Läufe unterschiedlicher User, When Indexierung endet, Then `updateFile(..., active)` schlägt nicht wegen **fremdem** User-Kontext fehl. | must |
-| AC5 | Given Workspace-Stream mit gültiger Instanz, When Chat-Interface und Agent-Chat-Service erzeugt werden, Then identische effektive `mandateId`/`featureInstanceId`-Kombination für den Chat-Cache. | should |
-| AC6 | Given `getWorkflow` scheitert an Datenvalidierung, When `createMessage` läuft, Then Log enthält **ursächliche** Exception, nicht nur „No access“. | should |
+| # | Kriterium (Given-When-Then) | Prio | Status |
+|---|---------------------------|------|--------|
+| AC1 | Given eine Datei mit nur Bild-`contentObjects` und aktiver Neutralisierung, When `indexFile` läuft, Then kein `UnboundLocalError` und definiertes Verhalten (OK/skip mit Log). | must | **done** |
+| AC2 | Given generiertes Tool-Schema für `ai_process`, When es an die OpenAI-kompatible API geht, Then `documentList` ist ein `array` **mit** `items` (kein 400 wegen fehlender `items`). | must | **done** |
+| AC3 | Given `listConnections` liefert eine Verbindungs-Referenz, When ein Outlook-Tool dieselbe Referenz nutzt, Then Verbindung wird aufgelöst (Format `connection:…` oder UUID-Fallback). | must | **done** |
+| AC4 | Given zwei parallele Uploads/Auto-Index-Läufe unterschiedlicher User, When Indexierung endet, Then `updateFile(..., active)` schlägt nicht wegen **fremdem** User-Kontext fehl. | must | **done** (Re-Acquire) |
+| AC5 | Given Workspace-Stream mit gültiger Instanz, When Chat-Interface und Agent-Chat-Service erzeugt werden, Then identische effektive `mandateId`/`featureInstanceId`-Kombination für den Chat-Cache. | should | **done** |
+| AC6 | Given `getWorkflow` scheitert an Datenvalidierung, When `createMessage` läuft, Then Log enthält **ursächliche** Exception, nicht nur „No access". | should | **done** |
## Testplan
@@ -118,6 +126,6 @@ Weitere Checkliste aus Template:
## Abschluss
+- [x] Alle Themen A–F umgesetzt (2026-04-11)
- [ ] b-reference/ aktualisiert — z. B. `b-reference/gateway/architecture.md` nur wenn Architektur-Entscheid (Singleton) dauerhaft geändert wird
- [ ] TOPICS.md aktualisiert (falls neues Thema)
-- [ ] Dieses Dokument bei Umsetzungsbeginn nach `c-work/2-build/` verschieben; nach Release nach `4-done/` bzw. `z-archive/`
diff --git a/c-work/4-done/2026-04-i18n-static-text-elimination.md b/c-work/4-done/2026-04-i18n-static-text-elimination.md
index a620eb9..9b69965 100644
--- a/c-work/4-done/2026-04-i18n-static-text-elimination.md
+++ b/c-work/4-done/2026-04-i18n-static-text-elimination.md
@@ -1,375 +1,4 @@
-
-
+
+
-# Statische Texte eliminieren — vollständige i18n-Migration
-
-## Fortschritt
-
-| Phase | Inhalt | Status |
-|-------|--------|--------|
-| 1 | Feature-Module (`mainXxx.py`), `registry.py`, `interfaceBootstrap.py` | **done** |
-| 2 | `nodeDefinitions/*`, `portTypes.py`, `nodeRegistry.py`, `entryPoints.py` | **done** |
-| 3 | Datamodel-Options (12 Dateien) + `SubscriptionPlan` title/description | **done** |
-| 4 | Accounting-Connectors, `frontendTypes` (keine `CUSTOM_TYPE_DESCRIPTIONS` mehr) | **done** |
-| 5 | Store, `mandate.ts` / Admin-Labels, Trustee-Views, `UnifiedDataBar`, `featuresApi` Mock | **done** |
-
-**Umsetzung 1–2:** Skript `local/scripts/_flatten_i18n_dicts.py` (mehrsprachige Dicts → deutscher String / `json.dumps`). Anschliessend manuell: `mainSystem.py` AICore-`providerLabels`-Fallback; `portTypes.PortField.description` auf `str`; `_deriveFormPayloadSchema` / `_deriveTransformSchema`; `entryPoints._normalize_title` liefert `str`. **`Role.description`** bleibt `TextMultilingual`: `coerce_text_multilingual()` in `datamodelUtils.py` wandelt Template-Strings/Dicts; Bootstrap- und Feature-`Role(...)`-Aufrufe nutzen `coerce_text_multilingual(...)`.
-
-**Umsetzung 3:** Flatten-Skript (Phase 3) für alle 12 Datamodel-Dateien. Neues Regex-Pattern `en, fr, de` für Trustee-Reihenfolge. Manuell: `SubscriptionPlan.title` und `.description` von `Dict[str, str]` auf `str` geändert; Downstream-Fixes in `routeSubscription.py`, `mainServiceSubscription.py`, `stripeBootstrap.py` (`.get("de")` → direkter String-Zugriff). `_registerDatamodelOptionLabels()` in `i18nRegistry.py` war bereits kompatibel (nutzt `_extractDe` das `str` und `dict` handelt).
-
-**Umsetzung 4:** `PHASE4` im Flatten-Skript; `frontendTypes.py`: `CUSTOM_TYPE_DESCRIPTIONS`, `getCustomTypeDescription`, `registerCustomType` entfernt (ungenutzt). **Umsetzung 5:** `Store.tsx` / `storeApi` / `routeStore`: Label & Description als deutsche i18n-Keys + `t()`; `FEATURE_REGISTRY` und `MandateFeature.label` als `string`; Hilfsfunktion `labelAsI18nKey()`; Admin-Seiten mit `t(labelAsI18nKey(...))`; `routeAdminFeatures.get_my_feature_instances`: `_featureLabelPlain()` liefert String-Labels statt Dict-Fallback.
-
-## Problemstellung
-
-Trotz Phase 1–7 der i18n-Unification existieren noch **~640+ statische mehrsprachige Dicts** (`{"en":…, "de":…, "fr":…}`) im Gateway und **~60+ Stellen** im Frontend. Diese Dicts:
-
-- Müssen bei jeder neuen Sprache manuell erweitert werden
-- Umgehen das zentrale i18n-System (`t()`, Admin-UI, AI-Übersetzung)
-- Sind inkonsistent (manche nur en/de, manche nur en/fr, manche en/de/fr)
-
-**Ziel:** Jeder UI-sichtbare Text wird als **deutscher Klartext-Key** gespeichert und zur Laufzeit über `t()` übersetzt. Mehrsprachige Dicts verschwinden komplett.
-
----
-
-## Architektur-Entscheidung
-
-### Prinzip: Deutscher Klartext = i18n-Key
-
-```
-# VORHER (statisch, 3 Sprachen hardcoded):
-"label": {"en": "Documents", "de": "Dokumente", "fr": "Documents"}
-
-# NACHHER (dynamisch, beliebig viele Sprachen):
-"label": "Dokumente"
-```
-
-Beim Boot registriert `_registerXxxLabels()` den deutschen Text als Key im `xx`-Basisset. `t("Dokumente")` liefert zur Laufzeit die Übersetzung in der aktuellen Sprache.
-
-### Sonderfälle
-
-| Fall | Lösung |
-|------|--------|
-| `outputLabels` (Listen von Strings pro Sprache) | Jedes Element einzeln als Key: `"Ja"`, `"Nein"` |
-| `_ISO_LABELS` (Sprachnamen) | Bleiben statisch — sind ISO-Referenzdaten, keine UI-Texte |
-| Locale-Code-Maps (`"de": "de-DE"`) | Bleiben statisch — technische Mappings |
-| `BUILTIN_PLANS` (Subscription) | Werden zu Keys, AI übersetzt |
-| Datamodel `frontend_options` | Labels werden zu Keys, Boot-Registrierung |
-
----
-
-## Datei-Index (vollständig)
-
-### Gateway — Feature-Module (Kategorie A: RBAC/Katalog-Labels)
-
-| # | Datei | Stellen | Muster | Phase |
-|---|-------|---------|--------|-------|
-| A1 | `features/trustee/mainTrustee.py` | ~62 | `DATA_OBJECTS`, `RESOURCE_OBJECTS`, `UI_OBJECTS`, `TEMPLATE_ROLES`, Workflow-Gruppen, Rollen-Beschreibungen | 1 |
-| A2 | `system/mainSystem.py` | ~27 | `DATA_OBJECTS`, LLM-Provider-Labels | 1 |
-| A3 | `features/commcoach/mainCommcoach.py` | ~17 | `DATA_OBJECTS`, `RESOURCE_OBJECTS`, `TEMPLATE_ROLES` | 1 |
-| A4 | `features/teamsbot/mainTeamsbot.py` | ~11 | `DATA_OBJECTS`, `RESOURCE_OBJECTS`, `TEMPLATE_ROLES` | 1 |
-| A5 | `features/workspace/mainWorkspace.py` | ~10 | `DATA_OBJECTS`, `RESOURCE_OBJECTS`, `TEMPLATE_ROLES` | 1 |
-| A6 | `features/chatbot/mainChatbot.py` | ~7 | `DATA_OBJECTS`, `TEMPLATE_ROLES` | 1 |
-| A7 | `features/neutralization/mainNeutralization.py` | ~7 | `DATA_OBJECTS`, `RESOURCE_OBJECTS` | 1 |
-| A8 | `features/realEstate/mainRealEstate.py` | ~6 | `DATA_OBJECTS`, `RESOURCE_OBJECTS` | 1 |
-| A9 | `features/graphicalEditor/mainGraphicalEditor.py` | ~6 | Permissions, Rollen-Beschreibungen | 1 |
-| A10 | `serviceCenter/registry.py` | ~14 | Service-Kategorie-Labels | 1 |
-| A11 | `interfaces/interfaceBootstrap.py` | ~4 | Template-Rollen-Beschreibungen (admin/user/viewer/sysadmin) | 1 |
-
-**Summe Kategorie A: ~171 Stellen, 11 Dateien**
-
-### Gateway — Graph-Editor Node-Definitionen (Kategorie B)
-
-| # | Datei | Stellen | Muster | Phase |
-|---|-------|---------|--------|-------|
-| B1 | `graphicalEditor/nodeDefinitions/clickup.py` | ~50 | Node-Label, Parameter-Descriptions | 2 |
-| B2 | `graphicalEditor/nodeDefinitions/input.py` | ~31 | Node-Label, Parameter-Descriptions | 2 |
-| B3 | `graphicalEditor/portTypes.py` | ~33 | Port-Feld-Descriptions | 2 |
-| B4 | `graphicalEditor/nodeDefinitions/sharepoint.py` | ~27 | Node-Label, Parameter-Descriptions | 2 |
-| B5 | `graphicalEditor/nodeDefinitions/email.py` | ~27 | Node-Label, Parameter-Descriptions | 2 |
-| B6 | `graphicalEditor/nodeDefinitions/ai.py` | ~23 | Node-Label, Parameter-Descriptions | 2 |
-| B7 | `graphicalEditor/nodeDefinitions/trustee.py` | ~20 | Node-Label, Parameter-Descriptions | 2 |
-| B8 | `graphicalEditor/nodeDefinitions/flow.py` | ~14 | Node-Label, `outputLabels` (Sonderfall: Liste) | 2 |
-| B9 | `graphicalEditor/nodeDefinitions/data.py` | ~9 | Node-Label, Parameter-Descriptions | 2 |
-| B10 | `graphicalEditor/nodeDefinitions/triggers.py` | ~8 | Node-Label, Parameter-Descriptions | 2 |
-| B11 | `graphicalEditor/nodeDefinitions/file.py` | ~7 | Node-Label, Parameter-Descriptions | 2 |
-| B12 | `graphicalEditor/nodeRegistry.py` | ~10 | Kategorie-Labels | 2 |
-| B13 | `graphicalEditor/entryPoints.py` | ~3 | Entry-Point-Titel | 2 |
-
-**Summe Kategorie B: ~262 Stellen, 13 Dateien**
-
-### Gateway — Datamodel-Options (Kategorie C)
-
-| # | Datei | Stellen | Muster | Phase |
-|---|-------|---------|--------|-------|
-| C1 | `datamodels/datamodelRbac.py` | ~19 | Scope/Access-Level Option-Labels (en/de/fr) | 3 |
-| C2 | `datamodels/datamodelChat.py` | ~7 | Status/Mode Option-Labels (en/fr, kein de!) | 3 |
-| C3 | `datamodels/datamodelMessaging.py` | ~11 | Channel/Status Option-Labels (en/fr) | 3 |
-| C4 | `datamodels/datamodelNotification.py` | ~8 | Type/Status Option-Labels (en/de) | 3 |
-| C5 | `datamodels/datamodelUam.py` | ~7 | Subscription-Status + Sprach-Options (en/de/fr) | 3 |
-| C6 | `datamodels/datamodelSubscription.py` | ~8 | Plan-Titel + Descriptions (en/de/fr, teils ohne fr) | 3 |
-| C7 | `datamodels/datamodelFiles.py` | ~4 | Scope Option-Labels (en/de) | 3 |
-| C8 | `datamodels/datamodelDataSource.py` | ~4 | Scope Option-Labels (en/de) | 3 |
-| C9 | `datamodels/datamodelFeatureDataSource.py` | ~4 | Scope Option-Labels (en/de) | 3 |
-| C10 | `datamodels/datamodelUiLanguage.py` | ~3 | Sync-Status Option-Labels (de/en) | 3 |
-| C11 | `features/trustee/datamodelFeatureTrustee.py` | ~13 | Währung + Dokumenttyp Option-Labels | 3 |
-| C12 | `features/neutralization/datamodelFeatureNeutralizer.py` | ~4 | Scope Option-Labels | 3 |
-
-**Summe Kategorie C: ~92 Stellen, 12 Dateien**
-
-### Gateway — Sonstige (Kategorie D)
-
-| # | Datei | Stellen | Muster | Phase |
-|---|-------|---------|--------|-------|
-| D1 | `shared/frontendTypes.py` | ~14 | `CUSTOM_TYPE_DESCRIPTIONS` (aktuell ungenutzt) | 4 |
-| D2 | `features/trustee/accounting/connectors/accountingConnectorBexio.py` | ~4 | Connector-Label + Feld-Labels | 4 |
-| D3 | `features/trustee/accounting/connectors/accountingConnectorRma.py` | ~4 | Connector-Label + Feld-Labels | 4 |
-| D4 | `features/trustee/accounting/connectors/accountingConnectorAbacus.py` | ~5 | Connector-Label + Feld-Labels | 4 |
-
-**Summe Kategorie D: ~27 Stellen, 4 Dateien**
-
-### Frontend (Kategorie E)
-
-| # | Datei | Stellen | Muster | Phase |
-|---|-------|---------|--------|-------|
-| E1 | `pages/Store.tsx` | ~5 | `FEATURE_DESCRIPTIONS` Dict (de/en/fr) | 5 |
-| E2 | `types/mandate.ts` | ~45 | Statische Navigation-Labels (de/en) | 5 |
-| E3 | `pages/views/trustee/TrusteeAbschlussView.tsx` | ~6 | Tile-Titel/Descriptions (de/en/fr) | 5 |
-| E4 | `pages/views/trustee/TrusteeAnalyseView.tsx` | ~4 | Tile-Descriptions (de/en) | 5 |
-| E5 | `components/UnifiedDataBar/UnifiedDataBar.tsx` | ~3 | `_TAB_LABELS` (de/en/fr) | 5 |
-| E6 | `api/featuresApi.ts` | ~4 | Mock-Labels (de/en) | 5 |
-| E7 | Diverse (12 Dateien) | ~12 | Hardcoded deutsche Strings ohne `t()` | 5 |
-
-**Summe Kategorie E: ~79 Stellen, ~18 Dateien**
-
-### Ausnahmen (bleiben statisch)
-
-| Datei | Grund |
-|-------|-------|
-| `routes/routeI18n.py` (`_ISO_LABELS`) | ISO-Referenzdaten (Sprachnamen), kein UI-Text |
-| `serviceAgent/coreTools/_mediaTools.py` | Locale-Code-Map (`"de"→"de-DE"`), technisch |
-| `serviceAgent/conversationManager.py` | Sprach-Name-Map für Agent-Kontext, technisch |
-
----
-
-## Gesamtübersicht
-
-| Kategorie | Dateien | Stellen | Komplexität |
-|-----------|---------|---------|-------------|
-| A: Feature-Module | 11 | ~171 | Mittel — uniformes Muster, braucht Boot-Registrierung |
-| B: Node-Definitionen | 13 | ~262 | Mittel — uniformes Muster, braucht eigene Registrierung |
-| C: Datamodel-Options | 12 | ~92 | Hoch — verschiedene Patterns, braucht generische Lösung |
-| D: Sonstige Gateway | 4 | ~27 | Niedrig — einfache Ersetzung |
-| E: Frontend | ~18 | ~79 | Niedrig — `t()` wrappen |
-| **Total** | **~58** | **~631** | |
-
----
-
-## Umsetzungsplan (5 Phasen)
-
-### Phase 1: Feature-Module (Kategorie A) — Composer-geeignet
-
-**Aufwand:** Mittel | **Modell:** Composer (schnelles Modell) | **Risiko:** Niedrig
-
-**Vorbereitung (einmalig, Opus):**
-- `_registerRbacLabels()` in `i18nRegistry.py` erweitern: neben `DATA_OBJECTS`, `RESOURCE_OBJECTS`, `TEMPLATE_ROLES` auch scannen:
- - `UI_OBJECTS[].label`
- - Workflow-Gruppen-Labels (`TRUSTEE_WORKFLOW_GROUPS`, `TRUSTEE_SERVICE_CATEGORIES`)
- - Rollen-`description` Blöcke
- - Service-Kategorie-Labels (`serviceCenter/registry.py`)
- - Bootstrap-Rollen (`interfaceBootstrap.py`)
-
-**Transformation (pro Datei, Composer):**
-Jedes `"label": {"en": "X", "de": "Y", "fr": "Z"}` wird zu `"label": "Y"` (deutscher Text).
-Jedes `"description": {"en": "X", "de": "Y", "fr": "Z"}` wird zu `"description": "Y"`.
-
-**Regel für Composer:**
-```
-In der Datei [DATEI]:
-- Ersetze jedes Dict {"en": "...", "de": "DEUTSCH", "fr": "..."} durch den deutschen Wert "DEUTSCH"
-- Ersetze jedes Dict {"de": "DEUTSCH", "en": "...", "fr": "..."} durch "DEUTSCH"
-- Wenn kein "de" vorhanden: nimm "en" Wert
-- Lasse alle anderen Felder unverändert
-```
-
-**Dateien:** A1–A11 (11 Dateien)
-
-**Akzeptanzkriterien:**
-- [ ] Kein `"fr":` mehr in den 11 Dateien (ausser Ausnahmen)
-- [ ] `_registerRbacLabels()` registriert alle neuen Key-Kategorien
-- [ ] Gateway startet fehlerfrei
-- [ ] Admin-UI Sprachen-Seite zeigt neue Keys im xx-Set
-
----
-
-### Phase 2: Node-Definitionen (Kategorie B) — Composer-geeignet
-
-**Aufwand:** Mittel | **Modell:** Composer (schnelles Modell) | **Risiko:** Niedrig
-
-**Vorbereitung (einmalig, Opus):**
-- Neue Funktion `_registerNodeLabels()` in `i18nRegistry.py`:
- - Scannt `STATIC_NODE_TYPES` aus `nodeDefinitions/__init__.py`
- - Registriert `label`, `description`, `parameters[].description`, `outputLabels[]` als Keys
- - Context: `node.label`, `node.desc`, `node.param`, `node.output`
-- Scannt `portTypes.PORT_TYPE_CATALOG` für Port-Feld-Descriptions
- - Context: `port.desc`
-- Scannt `nodeRegistry` Kategorie-Labels
- - Context: `node.category`
-- Scannt `entryPoints` Titel
- - Context: `node.entry`
-
-**Transformation (pro Datei, Composer):**
-Identisches Muster wie Phase 1: Dict → deutscher String.
-
-**Sonderfall `flow.py` `outputLabels`:**
-```python
-# VORHER:
-"outputLabels": {"en": ["Yes", "No"], "de": ["Ja", "Nein"], "fr": ["Oui", "Non"]}
-
-# NACHHER:
-"outputLabels": ["Ja", "Nein"]
-```
-
-**Dateien:** B1–B13 (13 Dateien)
-
-**Akzeptanzkriterien:**
-- [ ] Kein `"fr":` mehr in den 13 Dateien
-- [ ] `_registerNodeLabels()` registriert alle Node-Keys
-- [ ] Graph-Editor zeigt Nodes korrekt an (Labels übersetzt)
-- [ ] Node-Parameter-Descriptions im Config-Panel korrekt
-
----
-
-### Phase 3: Datamodel-Options (Kategorie C) — Opus nötig
-
-**Aufwand:** Hoch | **Modell:** Opus | **Risiko:** Mittel
-
-**Warum Opus:** Die Patterns sind uneinheitlich (en/fr, en/de, en/de/fr), die `frontend_options` Struktur variiert, und die Boot-Registrierung muss generisch über alle Datamodels funktionieren.
-
-**Vorbereitung:**
-- Neue generische Funktion `_registerDatamodelOptionLabels()` in `i18nRegistry.py`:
- - Scannt alle Pydantic-Modelle mit `json_schema_extra` → `frontend_options`
- - Extrahiert `label` Dicts und registriert den deutschen (oder englischen) Text als Key
- - Context: `option.{ModelName}.{fieldName}`
-- `BUILTIN_PLANS` in `datamodelSubscription.py`: `title`/`description` zu Keys
-
-**Transformation (pro Datei):**
-```python
-# VORHER:
-{"value": "active", "label": {"en": "Active", "de": "Aktiv", "fr": "Actif"}}
-
-# NACHHER:
-{"value": "active", "label": "Aktiv"}
-```
-
-**Problem: Fehlende Sprachen.** Einige Dicts haben kein `"de"` (z.B. `datamodelChat.py` nur en/fr). Hier wird `"en"` als Fallback genommen und der englische Text als Key registriert — die AI-Übersetzung erzeugt dann `de` und `fr`.
-
-**Dateien:** C1–C12 (12 Dateien)
-
-**Akzeptanzkriterien:**
-- [ ] Kein `"fr":` / `"en":` Dict-Pattern mehr in den 12 Dateien
-- [ ] `_registerDatamodelOptionLabels()` registriert alle Option-Keys
-- [ ] Frontend Select-Felder zeigen korrekte Labels
-- [ ] Subscription-Plan-Texte korrekt übersetzt
-
----
-
-### Phase 4: Sonstige Gateway (Kategorie D) — Composer-geeignet
-
-**Aufwand:** Niedrig | **Modell:** Composer (schnelles Modell) | **Risiko:** Niedrig
-
-**Transformation:**
-- `frontendTypes.py`: `CUSTOM_TYPE_DESCRIPTIONS` komplett entfernen (ungenutzt) + `getCustomTypeDescription()` und `registerCustomType()` vereinfachen oder entfernen
-- Accounting-Connectors: `displayName()` und Feld-Labels zu deutschen Strings
-
-**Dateien:** D1–D4 (4 Dateien)
-
-**Akzeptanzkriterien:**
-- [ ] `CUSTOM_TYPE_DESCRIPTIONS` entfernt
-- [ ] Accounting-Connector-Labels als deutsche Strings
-- [ ] Kein `"fr":` mehr in den 4 Dateien
-
----
-
-### Phase 5: Frontend (Kategorie E) — Composer-geeignet
-
-**Aufwand:** Niedrig | **Modell:** Composer (schnelles Modell) | **Risiko:** Niedrig
-
-**Transformation:**
-- Alle `{"en": "X", "de": "Y", "fr": "Z"}` Dicts durch `t("Y")` ersetzen
-- Alle hardcodierten deutschen Strings in JSX-Attributen mit `t()` wrappen
-- `mandate.ts` Navigation-Labels: zu `t()`-Keys (Backend liefert bereits i18n-Keys)
-- `_TAB_LABELS` in `UnifiedDataBar.tsx`: zu `t()`-Aufrufen
-- `FEATURE_DESCRIPTIONS` in `Store.tsx`: zu `t()`-Keys
-
-**Dateien:** E1–E7 (~18 Dateien)
-
-**Akzeptanzkriterien:**
-- [ ] Kein statisches `de:`/`en:`/`fr:` Dict-Pattern im Frontend
-- [ ] Alle UI-Texte via `t()` getaggt
-- [ ] Sprache wechseln → alle Texte ändern sich
-
----
-
-## Composer-Anweisungen (Copy-Paste-fertig)
-
-### Für Phase 1 + 2 + 4 (uniforme Dict→String Ersetzung):
-
-```
-Aufgabe: Ersetze in [DATEI] alle statischen mehrsprachigen Dicts durch den deutschen Klartext-String.
-
-Regeln:
-1. {"en": "...", "de": "DEUTSCH", "fr": "..."} → "DEUTSCH"
-2. {"de": "DEUTSCH", "en": "...", "fr": "..."} → "DEUTSCH"
-3. {"en": "ENGLISH", "fr": "..."} (kein "de") → "ENGLISH"
-4. {"en": "ENGLISH", "de": "DEUTSCH"} (kein "fr") → "DEUTSCH"
-5. Listen-Sonderfall: {"en": [...], "de": [LISTE], "fr": [...]} → [LISTE]
-6. Nur "label", "description", "title", "displayName" Felder ändern
-7. Keine Imports, Funktionssignaturen oder Logik ändern
-8. Keine Kommentare hinzufügen
-```
-
-### Für Phase 5 (Frontend t()-Wrapping):
-
-```
-Aufgabe: Ersetze in [DATEI] alle statischen Texte durch t()-Aufrufe.
-
-Regeln:
-1. {"en": "...", "de": "DEUTSCH", "fr": "..."} → t("DEUTSCH")
-2. Hardcoded string "DEUTSCH" in label/title/placeholder/aria-label → t("DEUTSCH")
-3. Import { useLanguage } from '...LanguageContext' hinzufügen falls fehlend
-4. const { t } = useLanguage(); in der Komponente falls fehlend
-5. Keine Logik ändern, nur Texte wrappen
-```
-
----
-
-## Reihenfolge und Abhängigkeiten
-
-```
-Phase 1 ──→ Phase 2 ──→ Phase 3 ──→ Phase 4
- (A) (B) (C) (D)
- │
- ▼
- Phase 5
- (E)
-```
-
-- Phase 1 muss zuerst: erweitert `_registerRbacLabels()` als Grundlage
-- Phase 2 nach 1: braucht das Pattern von Phase 1
-- Phase 3 nach 2: komplexeste Phase, braucht neue generische Registrierung
-- Phase 4 + 5: unabhängig voneinander, nach Phase 1
-
----
-
-## Aufwandsschätzung
-
-| Phase | Modell | Dateien | Geschätzte Zeit |
-|-------|--------|---------|-----------------|
-| 1 (Vorbereitung) | Opus | 1 | 15 min |
-| 1 (Transformation) | Composer | 11 | 30 min |
-| 2 (Vorbereitung) | Opus | 1 | 15 min |
-| 2 (Transformation) | Composer | 13 | 30 min |
-| 3 | Opus | 13 | 45 min |
-| 4 | Composer | 4 | 10 min |
-| 5 | Composer | 18 | 30 min |
-| **Total** | | **~58** | **~3 Stunden** |
+Dieser Plan wurde nach **`c-work/2-build/2026-04-i18n-static-text-elimination.md`** verschoben. Bitte dort weiterarbeiten.
diff --git a/d-guides/coding-conventions.md b/d-guides/coding-conventions.md
index 8d9f026..38e18bf 100644
--- a/d-guides/coding-conventions.md
+++ b/d-guides/coding-conventions.md
@@ -66,6 +66,57 @@ return {"message": t("Datei erfolgreich hochgeladen", "api.routeFiles",
**Nicht** mit `t()` taggen: Log-Eintraege, AI-Prompts, interne technische Fehlermeldungen.
+### Statische Dicts mit i18n-Keys: t() auf Modul-Ebene registrieren
+
+Wenn ein Modul statische Dicts/Listen mit UI-sichtbaren Texten definiert (z.B. `BADGE_DEFINITIONS`, Level-Labels), muessen die Keys **auf Modul-Ebene** via `t()` registriert werden. Nur so erscheinen sie im `xx`-Basisset und koennen uebersetzt werden.
+
+```python
+from modules.shared.i18nRegistry import t
+
+# Keys auf Modul-Ebene registrieren (Import-Zeit)
+_KEYS = [t("Erste Session"), t("3-Tage-Serie"), t("Wochenserie")]
+
+# Dict mit deutschen Quelltexten als Strings (NICHT t() im Dict!)
+BADGE_DEFINITIONS = {
+ "first_session": {"label": "Erste Session", "icon": "star"},
+ "streak_3": {"label": "3-Tage-Serie", "icon": "fire"},
+}
+
+# Bei Runtime nochmals t() aufrufen fuer aktuelle Sprache
+def getBadgeDefinitions():
+ return {k: {**v, "label": t(v["label"])} for k, v in BADGE_DEFINITIONS.items()}
+```
+
+**Warum?** `t()` registriert Keys nur beim ersten Aufruf. Wenn `t()` nur in einer Funktion steht, wird der Key erst beim ersten Request registriert -- **nach** dem Boot-Sync. Der Key fehlt dann im `xx`-Set und kann nicht uebersetzt werden.
+
+### TextMultilingual-Felder: Backend loest auf
+
+`TextMultilingual`-Felder (User-Content in mehreren Sprachen, z.B. `Role.description`) werden im Backend via `_resolveTextMultilingual()` aufgeloest bevor sie ans Frontend geliefert werden. Das Frontend erhaelt immer einen **String**, nie ein Dict.
+
+```python
+from modules.shared.i18nRegistry import _getLanguage
+
+def _resolveTextMultilingual(value) -> str:
+ if isinstance(value, str):
+ return value
+ if isinstance(value, dict):
+ lang = _getLanguage()
+ return value.get(lang) or value.get("de") or value.get("en") or ""
+ return str(value) if value else ""
+```
+
+### Frontend: Keine Dict-Fallbacks fuer Labels
+
+Das Frontend darf **keine** `label?.de || label?.en || Object.values(...)` Ketten verwenden. Wenn das Backend korrekt arbeitet, sind alle Labels Strings. Defensive `typeof === 'object'` Checks verdecken Backend-Fehler und sind verboten.
+
+```tsx
+// FALSCH - verdeckt Backend-Fehler
+const name = typeof m.name === 'object' ? m.name.de || m.name.en : m.name;
+
+// RICHTIG - Backend liefert String
+const name = m.label || m.name || m.id;
+```
+
Fuer Route-Module gibt es den Shorthand `apiRouteContext`:
```python