wiki/b-reference/platform-core/architecture.md

31 KiB

Gateway -- Architektur

Überblick

Das Gateway ist das Python-Backend der PowerOn-Plattform (FastAPI, PostgreSQL). Es kapselt Mandanten- und Feature-Logik hinter einer REST-API und folgt einer klaren Schichtenfolge: Connectors sprechen externe Systeme und Datenbanken an, Interfaces normalisieren Zugriffe und DTOs, Services bündeln fachliche Operationen darauf, und das ServiceCenter löst diese Services pro Request mit einheitlichem Kontext auf. Routen in modules/routes/ sind der HTTP-Einstieg; Security-Middleware und gemeinsame Utilities liegen in modules/security/ bzw. modules/shared/. Feature-spezifische Domänen leben in modules/features/ als weitgehend autonome Module.

Modulstruktur

Unter platform-core/modules/ (Kontext-Audit):

Modul Zweck
aicore/ Model-Registry, Model-Selector, Provider-Plugins (Anthropic, OpenAI, Mistral, Perplexity, Tavily, PrivateLLM)
auth/ Authentifizierung, CSRF, Token-Refresh-Middleware, JWT
connectors/ DB-Connector (PostgreSQL), Provider-Subpakete (Microsoft, Google, ClickUp, FTP, Infomaniak), Ticket/Messaging/Geo-Konnektoren. Pro Provider registrieren ServiceAdapter (OutlookAdapter, OneDriveAdapter, SharepointAdapter, TeamsAdapter, CalendarAdapter, ContactsAdapter, GmailAdapter, DriveAdapter, KdriveAdapter, …) die UDB-Services. Adapter-Registry pro Connector ist _SERVICE_MAP
datamodels/ Pydantic-Datenmodelle (u. a. Ai, Billing, Chat, Content, Files, Knowledge, Rbac, Subscription, UiLanguage, Workflow)
features/ Feature-Module (autonome Domänen): workspace, graphicalEditor, commcoach, neutralization, realEstate, trustee, teamsbot
interfaces/ DB-Interfaces (App, Billing, Chat, Knowledge, Management, Subscription), AI-Objects, RBAC, Features, Messaging
dbHelpers/ DB-Hilfsfunktionen (Registry, Audit-Logger, FK-Resolver, Pagination, Multi-Tenant-Optimierungen)
nodeCatalog/ Neutraler Container (L2): nodeDefinitions, portTypes, nodeAdapter, entryPoints
routes/ REST-API-Routen (u. a. Admin, Billing, DataFiles, DataSources, i18n, Security, Store, System)
security/ RBAC (rbac.py, rbacCatalog.py), Root-Access
serviceCenter/ Zentrale Service-Orchestrierung (Registry, Resolver, Kontext, Haupt-Services, ServicesBag)
shared/ Gemeinsame Utilities (attributeUtils, i18nRegistry, Konfiguration, Logging, …)
system/ System-Konfiguration, Feature-Discovery (registry.py: loadFeatureMainModules, loadFeatureRouters)
workflows/ Workflow-Engine mit Methoden, Aktionen und konsolidiertem Scheduler
workflowAutomation/ System-Komponente (L5b Orchestrator): Graph-Editor, Execution Engine, Run-Scheduler

ServiceCenter (Kern-Orchestrierung)

Das ServiceCenter ist die zentrale Schicht, die Kern- und importierbare Services verbindet. Pro Request/Session wird ein ServiceCenterContext (serviceCenter/context.py) mitgeführt und propagiert u. a.:

  • user: User (gesamtes User-Objekt), mandateId, featureInstanceId
  • workflow_id, workflow, feature_code
  • requireNeutralization (optional) — Neutralisierungsflag auf Chat-Ebene

Die öffentliche API umfasst u. a. getService(key, context) zur Auflösung einer Service-Instanz; registrierte Services sind in registry.py als CORE_SERVICES und IMPORTABLE_SERVICES hinterlegt, mit feature-spezifischer Auflösung und optionaler Vorabladung (preWarm).

Services (serviceCenter/services/)

Service Hauptmodul (Auszug) Zweck
serviceAgent mainServiceAgent.py AI-Agent mit vielen Tools (u. a. Dateien, Suche, Container, Web, Mail)
serviceAi mainServiceAi.py AI-Call-Gateway: Neutralisierung, Billing-Preflight, Provider-Auswahl, Streaming, Extraktion, Generierung
serviceKnowledge mainServiceKnowledge.py Knowledge Store: Indexierung, semantische Suche, RAG-Kontext (buildAgentContext)
serviceBilling mainServiceBilling.py Billing-Checks, Transaktionen
serviceChat mainServiceChat.py Chat-Persistenz
serviceExtraction mainServiceExtraction.py Dokument-Extraktion (PDF, DOCX, XLSX, …)
serviceGeneration mainServiceGeneration.py Dokument-Generierung
serviceMessaging mainServiceMessaging.py E-Mail, Notifications
serviceSubscription mainServiceSubscription.py Subscription-Verwaltung
serviceWeb mainServiceWeb.py Web-Scraping

Hinweis (Agent): Agent-Tools liefern Rohdaten; Neutralisierung vor dem Versand an ein Sprachmodell erfolgt zentral im serviceAi (mainServiceAi.py), nicht in den Tools selbst.

Einordnung (Framework)

Daten- und Steuerfluss: Client oder Workflow → Service Center → Service → Interface → Connector → externes System. Das Service Center bündelt dabei Erkennbarkeit (Registry), Konfiguration und querschnittliche Aspekte wie Logging, RBAC-Objekt-Registrierung (registerServiceObjects) und konsistente Service-Lebenszyklen.

ServicesBag (kanonischer Service-Zugriff)

ARCHITEKTUR-REGEL: Es gibt genau EINEN Service-Bag -- ServicesBag in serviceCenter/services/serviceAgent/mainServiceAgent.py, exportiert via serviceCenter/__init__.py. KEINE Parallelstrukturen erstellen!

Der ServicesBag ist das einzige Objekt, ueber das Code auf Services zugreift. Er wird als self.services in Workflows, Agent-Tools, und Processing-Modulen propagiert.

Attribut Typ Zugriff
user User Direkt
mandateId str Direkt
featureInstanceId str Direkt
chat ChatService Property (lazy)
extraction ExtractionService Property (lazy)
rbac RbacClass Property (lazy)
getService(key) Service Methode (dynamisch)
canAccessService(key) bool Methode

Verbotene Patterns (NICHT verwenden):

  1. KEIN ServiceHub -- Die alte ServiceHub-Klasse (serviceCenter/serviceHub.py) wurde eliminiert (2026-06-08). Sie exponierte rohe DB-Interfaces (interfaceDbApp, interfaceDbComponent, interfaceDbChat) als blanke Attribute -- unkontrollierter Zugriff ohne Service-Kapselung.
  2. KEIN _WorkflowAutomationServiceHub -- Die alte parallele Service-Bag-Klasse in workflowAutomation/ wurde eliminiert und durch ServicesBag ersetzt.
  3. KEIN _ServicesAdapter -- Der alte Name fuer ServicesBag. Umbenannt 2026-06-08.
  4. KEINE blanken Interface-Properties auf Service-Bags (z.B. services.interfaceDbComponent). Zugriff auf DB-Operationen erfolgt AUSSCHLIESSLICH ueber Service-Methoden (z.B. services.chat.getFile(), services.chat.createFile()).
  5. KEIN defensiver getattr fuer garantierte Attribute (z.B. getattr(services, "chat", None) ist verboten wenn chat garantiert existiert).
  6. KEINE manuellen ServiceCenterContext-Konstruktionen in Consumer-Code. Der Kontext wird vom ServicesBag propagiert.

ChatService als Kapselungsschicht:

Der ChatService (serviceCenter/services/serviceChat/mainServiceChat.py) kapselt alle File/Folder-CRUD-Operationen und Workflow-Listing. Consumer greifen NIE direkt auf interfaceDbComponent oder interfaceDbChat zu, sondern immer ueber services.chat.*:

Methode Kapselt
getFile(fileId) interfaceDbComponent.getFile
getFileData(fileId) interfaceDbComponent.getFileData
createFile(name, mime, content) interfaceDbComponent.createFile
createFileData(fileId, data) interfaceDbComponent.createFileData
saveUploadedFile(content, name) interfaceDbComponent.saveUploadedFile
deleteFile(fileId) interfaceDbComponent.deleteFile
copyFile(sourceId, newName) interfaceDbComponent.copyFile
createFolder(name, parentId) interfaceDbComponent.createFolder
getWorkflows(pagination) interfaceDbChat.getWorkflows
getMessages(workflowId, pagination) interfaceDbChat.getMessages
createActionItem(actionData) interfaceDbChat._separateObjectFields + db.recordCreate

Interfaces (DB-Schicht)

Die genannten Module kapseln die Datenbankzugriffe bzw. die zugehörigen Fachverträge (Audit-Liste):

Interface Verantwortlich für
interfaceDbApp.py User, Mandate, FeatureAccess, UserConnections, Preferences
interfaceDbBilling.py BillingAccount, BillingTransaction, Subscriptions
interfaceDbChat.py ChatWorkflow, ChatMessage, ChatDocument
interfaceDbKnowledge.py FileContentIndex, ContentChunk, RoundMemory (RAG/Knowledge Store)
interfaceDbManagement.py FileItem, Folder, Prompt, DataSource (mandantenweite Stammdaten), UiLanguageSet-Seeding
interfaceDbSubscription.py Subscription-Verwaltung
interfaceAiObjects.py AI-Call-Abstraktion (Text, Embedding, Vision, Streaming)
interfaceFeatures.py Feature-Instanz-Lifecycle, Template-Rollen-Kopie
interfaceRbac.py RBAC-Regelauswertung

Weitere Interface-Dateien im Ordner (z. B. Voice, Tickets, Messaging, Bootstrap) erfüllen dieselbe Normalisierungsrolle gegenüber Connectors bzw. externen APIs; die Tabelle oben entspricht der expliziten DB-/Kern-Zuordnung aus dem Kontext-Audit.

Feature Lifecycle Hooks

Features sind autonome Module unter modules/features/<featureCode>/. Jedes Feature hat ein main<Feature>.py mit Registrierungs- und Lifecycle-Funktionen. Die Core-Schicht ruft diese dynamisch via system/registry.pyloadFeatureMainModules() auf — es gibt keine hardcodierten Feature-Imports in den Interfaces.

Pflicht-Funktionen (Feature-Registrierung)

Funktion Beschreibung
getFeatureDefinition() -> Dict Feature-Metadaten: code, label, icon, autoCreateInstance
registerFeature(catalogService) -> bool RBAC-Objekte (UI, Resource, Data) im Katalog registrieren
getUiObjects() -> List[Dict] UI-Objekte fuer RBAC-Sichtbarkeit
getResourceObjects() -> List[Dict] Resource-Objekte fuer RBAC
getTemplateRoles() -> List[Dict] Template-Rollen mit AccessRules

Optionale Lifecycle Hooks

Jedes Feature kann diese Funktionen in seinem main<Feature>.py definieren. Die Core-Schicht ruft sie automatisch auf, wenn die entsprechende Situation eintritt:

Hook Signatur Wann aufgerufen Wo aufgerufen
onMandateDelete (mandateId: str, instances: list) -> None Mandate wird geloescht (hard-delete) interfaceDbApp.py (Cascade-Delete-Loop)
onBootstrap () -> None App-Start (nach DB-Initialisierung) interfaceBootstrap.py (Boot-Sequenz)
onInstanceCreate (mandateId: str, instanceId: str, featureCode: str, templateWorkflows: list) -> int Feature-Instanz wird erstellt und das Feature hat Template-Workflows interfaceFeatures.py (via graphicalEditor)
onStart async (eventUser) -> None App-Start (Feature-spezifische Hintergrund-Prozesse) app.py (Startup-Event)
onStop async (eventUser) -> None App-Shutdown app.py (Shutdown-Event)
getTemplateWorkflows () -> List[Dict] Bootstrap / Instance-Create (Workflow-Templates bereitstellen) mainGraphicalEditor.onBootstrap() / onInstanceCreate()

Aufruf-Pattern (Core-Seite)

from modules.system.registry import loadFeatureMainModules

for featureCode, mod in loadFeatureMainModules().items():
    hook = getattr(mod, "onMandateDelete", None)
    if hook:
        try:
            hook(mandateId, instances)
        except Exception as e:
            logger.warning(f"onMandateDelete hook for '{featureCode}' failed: {e}")

Implementierungs-Status

Alle Features haben onMandateDelete implementiert:

Feature DB onMandateDelete onBootstrap onInstanceCreate Besonderheiten
graphicalEditor poweron_graphicaleditor Yes Yes Yes Vollstaendige Hooks
trustee poweron_trustee Yes 13 Models
commcoach poweron_commcoach Yes Nutzt instanceId statt featureInstanceId
teamsbot poweron_teamsbot Yes Nutzt instanceId; Child-Records via Session-FK
neutralization poweron_neutralization Yes 2 Models
workspace poweron_workspace Yes 1 Model
realEstate poweron_realestate Yes Fallback auf mandateId
redmine poweron_redmine Yes 3 Models inkl. Ticket-Mirrors

Beispiel: Neues Feature mit Lifecycle

# modules/features/myFeature/mainMyFeature.py

FEATURE_CODE = "myFeature"

def getFeatureDefinition() -> Dict[str, Any]:
    return {"code": FEATURE_CODE, "label": "My Feature", "icon": "mdi-star", "autoCreateInstance": False}

def onMandateDelete(mandateId: str, instances: list) -> None:
    """Eigene Daten aufraumen wenn ein Mandate geloescht wird."""
    # Hier: Verbindung zur eigenen DB, Records loeschen
    pass

def onBootstrap() -> None:
    """Eigene System-Daten seeden beim App-Start."""
    pass

Architektur-Prinzipien

  1. Kein Aufwaerts-Import: Die Core-Schicht (interfaces) importiert NIEMALS direkt aus features/. Alle Feature-spezifische Logik wird ueber Hooks und die Registry aufgerufen.
  2. Feature raeumt selbst auf: Jedes Feature ist verantwortlich fuer seine eigenen Daten (eigene DB, eigene Models). Die Core-Schicht delegiert Lifecycle-Events.
  3. Fail-safe: Hook-Fehler werden geloggt, brechen aber nicht den Boot oder die Mandate-Deletion ab.
  4. Dynamisch erweiterbar: Neue Features muessen nur die Hook-Funktion definieren — kein Code in der Core-Schicht muss angepasst werden.

Workflow-Automation Models

Die Workflow-Engine-Models leben kanonisch in datamodels/datamodelWorkflowAutomation.py:

Model Beschreibung DB
AutoWorkflow Workflow-Definition (Graph, Triggers, Templates) poweron_graphicaleditor
AutoVersion Versionierter Graph-Snapshot poweron_graphicaleditor
AutoRun Laufende/abgeschlossene Ausfuehrung poweron_graphicaleditor
AutoStepLog Protokoll pro Knoten-Ausfuehrung poweron_graphicaleditor
AutoTask Human Tasks (Formulare, Approvals) poweron_graphicaleditor

Die DB-Konstante WORKFLOW_AUTOMATION_DATABASE = "poweron_graphicaleditor" ist ebenfalls dort definiert (DB-Name ist historisch, Konstante wurde umbenannt 2026-06-08). Feature-interne Re-Exports in features/graphicalEditor/datamodelFeatureGraphicalEditor.py existieren fuer Backward-Kompatibilitaet.

Schlüssel-Dateien

Datei / Pfad Rolle
platform-core/app.py FastAPI-Anwendung (generischer Entry-Point), Routen, Middleware, EventManager, Scheduler-MainLoop
platform-core/modules/serviceCenter/__init__.py Service Center: getService, preWarm, RBAC-Registrierung für Service-Objekte
platform-core/modules/serviceCenter/context.py ServiceCenterContext pro Request
platform-core/modules/serviceCenter/registry.py Service-Registry (CORE / IMPORTABLE)
platform-core/modules/serviceCenter/resolver.py Auflösung von Service-Instanzen inkl. Cache
platform-core/modules/serviceCenter/services/serviceAgent/mainServiceAgent.py ServicesBag -- kanonischer Service-Bag fuer Workflows/Agent/Processing
platform-core/modules/routes/*.py HTTP-Endpunkte, Aufruf in Richtung Service Center / Features (inkl. routeI18n.py für DB-backed i18n mit AI-Übersetzung)
platform-core/modules/interfaces/*.py Stabile Verträge und DB-Zugriffe (keine direkte Vendor-Logik in Services)
platform-core/modules/connectors/*.py Vendor-spezifische Adapter (Auth, Transport, Mapping)
platform-core/modules/security/* RBAC-Auswertung, RBAC-Katalog, Root-Access
platform-core/modules/auth/* CSRF, Token-Refresh-Middleware, JWT, OAuth
platform-core/modules/shared/* Querschnitt: Konfiguration, Audit-Logging, Events, Utilities
platform-core/modules/system/registry.py Feature-Discovery, Router-Laden, Katalog-Registrierung beim App-Start
platform-core/modules/workflows/workflowManager.py Zentrale Workflow-Steuerung (Workspace/Chat Workflows)
platform-core/modules/workflowAutomation/engine/executionEngine.py Graph-Execution-Engine: topoSort, Transit-Routing, _normalizeToSchema nach Execute, flow.merge-Wait, Resume-Schema-Validierung
platform-core/modules/workflowAutomation/scheduler/mainScheduler.py Konsolidierter Workflow-Scheduler
platform-core/modules/interfaces/interfaceBootstrap.py System-Bootstrap (Templates, Billing, Stripe)
platform-core/modules/interfaces/interfaceVoiceObjects.py Fassade Google STT/TTS, Billing-Callback Streaming
platform-core/modules/connectors/connectorVoiceGoogle.py Google Speech v1 + Translation + TTS-Client
platform-core/modules/routes/routeVoiceGoogle.py /voice-google/* inkl. STT-Streaming-WebSocket

Google Voice (STT / TTS)

Siehe Kanon-Seite voice-google.md (Batch- vs Streaming-Pfad, WS-Protokoll open/end_of_single_utterance, Parameter model / lightweight / audioFormat, Zuordnung CommCoach / Teamsbot / Workspace / Agent-Tools).

i18n (Mehrsprachigkeit)

Das Gateway nutzt ein DB-basiertes Sprachsystem (UiLanguageSet). Alle UI-sichtbaren Texte (HTTPException-Details, Model-Labels, API-Messages) werden ueber t() getaggt und per AI uebersetzt.

Komponente Datei Zweck
i18nRegistry.py modules/shared/ t(), resolveText(), @i18nModel Decorator, _REGISTRY, _CACHE, _setLanguage, Boot-Sync
attributeUtils.py modules/shared/ getModelLabels() / getModelLabel() lesen aus i18nRegistry.MODEL_LABELS, nutzen resolveText()
routeI18n.py modules/routes/ Admin-API: Sprachset-CRUD, AI-Uebersetzung, xx-Sync, TextMultilingual-Batch-Uebersetzung
app.py platform-core/ Boot-Hooks (_syncRegistryToDb, _loadCache), Request-Middleware (_setLanguage)

Architektur-Regeln

  1. t() NUR mit String-Literalen: t("Speichern") ist korrekt, t(variable) ist verboten. Jeder i18n-Key muss als Literal im Code stehen, damit er beim Import registriert wird.
  2. Backend uebersetzt, Frontend rendert: Das Backend liefert uebersetzte Strings via API. Das Frontend ruft t() nur fuer eigene statische UI-Texte auf, nie fuer Werte die vom Backend kommen.
  3. Fallback [key]: Wenn eine Uebersetzung fehlt, gibt t() [key] zurueck (z.B. [Speichern]), damit fehlende Uebersetzungen im UI sofort sichtbar sind.
  4. Keine lokalen Resolve-Helfer: Alle Label-Aufloesung laeuft ueber resolveText() (zentral in i18nRegistry.py). Keine _resolveLabel(), _resolveTextMultilingual(), _storeLabelText() etc. in Route-Dateien.

Zentrale Funktionen

t(key: str) -> str — Tag + Translate:

  • Bei Import: registriert Key in _REGISTRY
  • Bei Runtime: liefert _CACHE[lang][key], Fallback [key]
  • Sprache kommt aus _CURRENT_LANGUAGE ContextVar (gesetzt durch Middleware)

resolveText(value: Any, lang: Optional[str] = None) -> str — Universelle Label-Aufloesung:

  • strt(value) (behandelt als i18n-Key)
  • dict → Nutzersprache aus Kontext (oder lang wenn gesetzt), dann xx, dann erster nicht-leerer Wert (keine eckigen Klammern — das ist Inhalt, kein fehlender UI-Key)
  • TextMultilingualmodel_dump() dann dict-Logik
  • None""

Boot-Reihenfolge

  1. Module laden → @i18nModel Decorators + t()-Aufrufe registrieren Keys in _REGISTRY
  2. _syncRegistryToDb():
    • Scannt Route-Dateien nach routeApiMsg("…") (api.* Keys)
    • _registerNavLabels(): Navigation-Labels aus NAVIGATION_SECTIONS (nav.* Keys)
    • _registerFeatureUiLabels(): FEATURE_LABEL und UI_OBJECTS Labels (nav.* Keys)
    • _registerRbacLabels(): DATA_OBJECTS, RESOURCE_OBJECTS, TEMPLATE_ROLES, QUICK_ACTIONS Labels (rbac.data, rbac.resource, rbac.role, rbac.quickaction Keys)
    • Merged Gateway-Keys ins xx-Basisset (UI-Keys bleiben erhalten)
  3. _loadCache(): Laedt alle UiLanguageSets in den In-Memory-Cache (_CACHE)

Request-Flow

Middleware setzt _setLanguage(lang) aus Accept-Language Header → t("Key") liefert Uebersetzung aus _CACHE[lang] (O(1) Dict-Lookup, kein DB-Call). resolveText() nutzt denselben Mechanismus.

TextMultilingual

Felder vom Typ TextMultilingual speichern Benutzertexte mehrsprachig. xx ist das Pflichtfeld (Quelltext/Deutsch), weitere Sprachen werden dynamisch als Extra-Felder gespeichert. Bei neuer Sprache werden alle TextMultilingual-Felder per Batch-Job automatisch uebersetzt (routeI18n._translateTextMultilingualFields). On-Demand: POST /api/i18n/translate-field.

Context-Namensraeume

ui (Frontend), api.<routeModuleName> (HTTPExceptions), table.<ClassName> (Model-Labels), table.<ClassName>.<fieldName> (Feld-Labels), nav (Navigation/Feature-Labels), rbac.data / rbac.resource / rbac.role / rbac.quickaction (RBAC-Katalog)

Entries-Identitaet: Ein Entry wird durch (key, context) eindeutig identifiziert — derselbe Text kann mit verschiedenen Contexts existieren.

BackgroundJob-Progress-Messages

Hintergrundjobs laufen ausserhalb des Request-Kontexts und haben deshalb keinen _CURRENT_LANGUAGE-Wert. Walker schreiben deshalb einen strukturierten i18n-Payload in die DB und der Route-Handler uebersetzt server-side beim Read -- der Frontend ruft t() nie auf Backend-supplied Werten auf (Regel #2).

1. Walker schreibt strukturiert (platform-core/modules/serviceCenter/.../subConnectorSync*.py):

progressCb(
    0,
    messageKey="{n} Dateien verarbeitet, {indexed} indexiert",
    messageParams={"n": processed, "indexed": result.indexed},
)

messageKey ist ein String-Literal (Pflicht, damit es scanbar bleibt). JobProgressCallback speichert {key, params} in BackgroundJob.progressMessageData (JSONB) und einen deutschen Best-Effort-Fallback in progressMessage (fuer Logs/Audit/Legacy).

2. Key-Registrierung -- progressCb(..., messageKey="…") durchlaeuft NICHT t(), deshalb braucht jeder Key ein string-literales t("…") in der Feature-Registrierungsdatei (siehe serviceKnowledge/_progressMessages.py, features/trustee/mainTrustee.py). KEINE Schleifen ueber Listen mit t(variable) -- jede Zeile muss t("LITERAL") sein, sonst nimmt der Boot-Scan den Key nicht in UiLanguageSet auf.

3. Route uebersetzt server-side (routeJobs._serialiseJob, routeRagInventory):

from modules.shared.i18nRegistry import resolveJobMessage

out["progressMessage"] = (
    resolveJobMessage(j.get("progressMessageData"))
    or j.get("progressMessage", "")
)

resolveJobMessage(messageData) ruft intern t(key) mit der Request-Sprache und substituiert die Params. Das ist analog zu resolveText(value) -- es ist der einzige zulaessige t(variable)-Pfad, weil er innerhalb der i18n-Infrastruktur liegt.

4. Frontend rendert 1:1 -- progressMessage ist bereits uebersetzt, die Render-Stellen lesen das Feld direkt:

<span>{conn.runningJobs[0].progressMessage || t('Synchronisierung läuft...')}</span>

Das t('Synchronisierung läuft...') ist ein lokales UI-Fallback (string-literal, ok), das progressMessage aus dem Backend geht nicht durch t().

Feature: Trustee -- Daten-Tabellen-Endpunkte

Alle 13 Trustee-Tabellen sind ueber paginierte, RBAC-gefilterte GET-Endpunkte abrufbar. Die sechs CRUD-Modelle (TrusteeOrganisation, TrusteeRole, TrusteeAccess, TrusteeContract, TrusteeDocument, TrusteePosition) haben weiterhin die etablierten REST-Routen; sieben weitere (zuvor nur als JSON-Export oder Aggregat-Endpunkt verfuegbar) wurden ergaenzt:

Endpunkt Modell Zweck
GET /api/trustee/{instanceId}/data/accounts TrusteeDataAccount Synchronisierter Kontenplan (read-only)
GET /api/trustee/{instanceId}/data/journal-entries TrusteeDataJournalEntry Synchronisierte Buchungskoepfe (read-only)
GET /api/trustee/{instanceId}/data/journal-lines TrusteeDataJournalLine Synchronisierte Buchungszeilen (read-only)
GET /api/trustee/{instanceId}/data/contacts TrusteeDataContact Synchronisierte Kontakte (read-only)
GET /api/trustee/{instanceId}/data/account-balances TrusteeDataAccountBalance Synchronisierte Saldenliste (read-only)
GET /api/trustee/{instanceId}/accounting/configs TrusteeAccountingConfig Connector-Konfiguration (read-only; Secrets maskiert)
GET /api/trustee/{instanceId}/accounting/syncs TrusteeAccountingSync Audit-Eintraege der Sync-Laeufe (read-only)

Alle sieben Endpunkte teilen sich den Helper _paginatedReadEndpoint (routeFeatureTrustee.py), der das Pattern aus get_documents / get_positions reproduziert: Unified Filter API (mode=filterValues|ids), _validateInstanceAccess fuer Instanz-Gating und getRecordsetPaginatedWithRBAC mit featureCode + data.feature.trustee.<Model> fuer datenbasierten RBAC. FK-Felder werden automatisch via enrichRowsWithFkLabels aufgeloest (siehe FK Label Resolution).

UI-seitig sind diese Endpunkte unter /mandates/{m}/trustee/{i}/data-tables[?tab=<key>] (View TrusteeDataTablesView) zugaenglich; UI-Sichtbarkeit der Seite haengt am Permission-Eintrag ui.feature.trustee.data-tables (Template-Rollen trustee-viewer, trustee-user, trustee-accountant, Admin via Wildcard).

Typed Action Architecture (4-Schichten)

Seit Phase 1-5 (April 2026) folgt jede Workflow-Aktion einem strikten 4-Schichten-Modell. Quellen: wiki/c-work/3-validate/2026-04-typed-action-architecture.md, Tests in platform-core/tests/unit/workflows/, platform-core/tests/integration/trustee/.

Schicht Modul / Verantwortung Type-Anker
1. Editor (Frontend) ui-nyla/src/components/FlowEditor/nodes/shared/ -- RequiredAttributePicker, DataPicker (strict-mode + Object-Drill-Down mit *-Wildcard), paramValidation.ts Action-Signaturen aus dem Catalog (/api/workflow-automation/catalog)
2. Action / Method platform-core/modules/workflows/methods/method<Feature>/actions/*.py (extractFromFiles, processDocuments, syncToAccounting, ...) Typisierte Pydantic-Eingabe + ActionResult-Output, FeatureInstanceRef als Discriminator-Envelope
3. Adapter platform-core/modules/nodeCatalog/nodeDefinitions/registerNodeWithMethod -- leitet Node-Definitions aus Action-Signaturen ab, liefert Snapshot fuer den Editor (adapter_validator) Snapshot-Test test_staticNodesHaveNoDriftAgainstLiveMethods (_KNOWN_ADAPTER_DRIFTS = frozenset())
4. Runtime platform-core/modules/workflowAutomation/engine/executionEngine.py -- executeGraph mit materializeFeatureInstanceRefs + materializeConnectionRefs, _unwrapTypedRef fuer Action-Calls Migrations-Helper in workflowAutomation/engine/featureInstanceRefMigration.py + DB-CLI scripts/script_migrate_feature_instance_refs.py

Konsequenzen:

  • Single source of truth ist die Action-Signatur. Editor, Adapter, Runtime und AI-Agent (actionToolAdapter) lesen alle dieselbe Catalog-Sicht.
  • Pick-not-push auf Schicht 4: Runtime resolviert connectionReference und unwrappt typisierte Envelopes (FeatureInstanceRef, DataRef) automatisch -- Legacy-Aktionen bleiben funktionsfaehig.
  • Save-with-errors ist explizit erlaubt (AC-9-Patch im CanvasHeader); Run wird mit executeBlockedReason blockiert, der Save-Toast meldet Pflicht-Fehler-Anzahl.
  • DB-Hygiene ist optional via script_migrate_feature_instance_refs.py (--dry-run standard) -- ohne Skript laufen Workflows korrekt, der Editor zeigt aber bis zum naechsten Save noch das Legacy-Format.

Layer-Hierarchie (strikte Importrichtung)

shared (L0)              →  0 ausgehende Imports
datamodels (L1)          →  nur shared
connectors (L2)          →  shared, datamodels
nodeCatalog (L2)         →  shared, datamodels
dbHelpers (L3)           →  shared, datamodels, connectors
aicore (L3)              →  shared, datamodels, connectors
interfaces (L4)          →  L0-L3 + security, system, nodeCatalog
system (L4)              →  L0-L3 + nodeCatalog
security (L4)            →  L0-L3
auth (L4)                →  L0-L4
serviceCenter (L5a)      →  L0-L4
workflows (L5a)          →  L0-L4
features (L5a)           →  L0-L4 + serviceCenter, workflows
workflowAutomation (L5b) →  L0-L5a (Orchestrator, darf von L5a importieren)
routes (L6)              →  L0-L5b
demoConfigs (L6)         →  L0-L5b
app (L7)                 →  alle (Composition Root)

Jede Schicht importiert NUR von niedrigeren Schichten. workflowAutomation (L5b) ist der Orchestrator ueber workflows/serviceCenter/features — Imports VON L5a NACH L5b sind VERBOTEN (keine Rueckkanten). Alle frueheren Violations (interfaces→WA, system→WA, serviceCenter→WA, workflows↔serviceCenter bidirektional) sind seit 2026-06-08 geloest. Seit 2026-06-09 sind auch alle Intra-Modul-Zyklen (bidirektionale Imports zwischen Dateien innerhalb desselben Modul-Ordners) bereinigt — u.a. via Protocol-Typen in serviceCenter/core/types.py, Logik-Extraktion (flagResolution, _valueKindResolver, _runNotifications) und Lifecycle-Hook-Patterns.

Regeln / Invarianten

  • Schichten: Connectors sind anbieterspezifisch und ersetzbar; Services hängen von Interfaces ab, nicht direkt von Connectors. Geschäftslogik und Guardrails liegen in den Services.
  • Kein Aufwaerts-Import: Interfaces importieren NICHT von features, routes oder serviceCenter. Feature-Interaktion laeuft ueber Lifecycle Hooks (siehe oben).
  • Datenbank: Persistenz und die genannten Domänen-Reads/Writes laufen über die Interface-Schicht, nicht ad hoc aus Routen heraus.
  • Service Center: Aufrufe laufen über die zentrale Auflösung (getService + Kontext); Workflows und Routen konsumieren dieselbe Landschaft.
  • Sicherheit & Governance: RBAC und Geheimnisse werden zentral geführt; Service-Aufrufe und Workflow-Schritte sollen nachvollziehbar sein (Audit); Kontingente und Limits pro Mandant/Service sind vorgesehen.
  • HTTP-Einstieg: Globale Middleware (CSRF, Token-Refresh unter modules/auth/); RBAC-Auswertung und Katalog unter modules/security/.
  • Code-Konventionen (Projekt): Interne Hilfsfunktionen mit Präfix _ (duerfen NICHT extern importiert werden); Bezeichner in camelCase für Variablen und Funktionen.