From 14dbd418a62293493997545107e39f3d6f0bc3f5 Mon Sep 17 00:00:00 2001
From: ValueOn AG
Date: Sun, 5 Apr 2026 02:01:15 +0200
Subject: [PATCH] automation concept
---
concepts/Automation Business Spec.md | 740 ++++++++++++++++++++
concepts/Automation Data Model.md | 994 +++++++++++++++++++++++++++
2 files changed, 1734 insertions(+)
create mode 100644 concepts/Automation Business Spec.md
create mode 100644 concepts/Automation Data Model.md
diff --git a/concepts/Automation Business Spec.md b/concepts/Automation Business Spec.md
new file mode 100644
index 0000000..2c67231
--- /dev/null
+++ b/concepts/Automation Business Spec.md
@@ -0,0 +1,740 @@
+# PowerOn Automation Unification — Business Specification
+
+**Version:** 2.0
+**Datum:** 2026-04-05
+**Status:** Draft
+
+---
+
+## 1. Executive Summary
+
+PowerOn enthält heute drei teilweise überlappende Systeme für die Automatisierung von Workflows. Die Ziel-Architektur konsolidiert diese zu einer einheitlichen Platform mit klaren Grenzen zwischen Services, Features und Shared Libraries.
+
+**Leitprinzipien:**
+- Keine Migration von Altdaten — Automation v1 ist nicht produktiv, wird nicht migriert
+- Automation v1 bleibt bestehen, bis es durch das neue Feature abgelöst ist
+- Klare Datenmodelle mit definierten State Machines als stabile, erweiterbare Grundlage
+- Klare Trennung von Mandant/Feature/Feature-Instanz und zugehöriger RBAC-Logik
+- Skalierbare AI-Tool-Architektur mit Toolboxes und Sub-Agents für 50–100+ Tools
+
+---
+
+## 2. Ist-Analyse
+
+### 2.1 Drei bestehende Systeme
+
+| System | Modell | Stärke | Schwäche |
+|--------|--------|--------|----------|
+| **Automation v1** | Template + Cron + Placeholders | Stabiles, bewährtes Scheduling mit Callback-basiertem Sync, inkrementeller Job-Registrierung, Execution-Logs | Kein visuelles Editing, kein Branching, starres Template-Modell |
+| **Automation2** | Graph-Editor (n8n-style) + Run/Task-Modell | Visuelles Modellieren, Branching, Loops, Human-in-the-loop, Pause/Resume, diverse Node-Typen | Kein AI-Chat im Editor, kein UDB-Zugang, Scheduler-Sync weniger robust als v1 |
+| **AI Workspace** | Chat-driven AI Agent mit Tools | Natural-language Interaktion, 40+ Core Tools + Action Tools, Streaming, RAG | Kein persistiertes Workflow-Modell, kein Scheduling |
+
+### 2.2 Wer nutzt die Method/Action Library?
+
+Die `workflows/methods/` Library (methodAi, methodOutlook, methodSharepoint, etc.) ist bereits die de facto gemeinsame Basis. Alle Systeme nutzen `ActionExecutor.executeAction()`:
+
+| Consumer | Pfad zur Action Library |
+|----------|------------------------|
+| **Workspace AI Agent** | `ActionToolAdapter` registriert `dynamicMode=True` Actions als Agent-Tools → LLM wählt Tool → `ActionExecutor` |
+| **Automation2 Graph Engine** | `ActionNodeExecutor` liest `_method`/`_action` aus Node-Definition → `ActionExecutor` |
+| **Automation v1** | `WorkflowProcessor` → `modeAutomation` → `ActionExecutor` |
+| **Workspace Dynamic Mode** | `WorkflowProcessor` → `modeDynamic` → AI plant Tasks → `ActionExecutor` |
+
+### 2.3 Wer nutzt die AI Tools?
+
+| Tool-Typ | Registrierung | Nutzung |
+|----------|--------------|---------|
+| **Core Tools** (40: readFile, webSearch, sendMail, etc.) | `_registerCoreTools()` | Workspace Agent, CommCoach Agent |
+| **Action Tools** (dynamicMode Actions) | `ActionToolAdapter.registerAll()` | Workspace Agent (toolSet-übergreifend) |
+| **Graph Node Types** (ai.prompt, email.checkEmail, etc.) | `STATIC_NODE_TYPES` in `nodeDefinitions/` | Automation2 `executeGraph` |
+
+**Benötigen die AI Tools die Method/Actions?** Ja, direkt. `ActionToolAdapter` transformiert jede `dynamicMode=True` Action in ein Agent-Tool. Und die Automation2-Nodes mappen über `_method`/`_action` auf dieselbe Library.
+
+### 2.4 Identifizierte Inkonsistenzen
+
+| # | Inkonsistenz |
+|---|-------------|
+| **I-1** | Zwei Datenmodelle: `AutomationDefinition` (v1) vs `Automation2Workflow` (v2) — kein gemeinsames Workflow-Konzept |
+| **I-2** | Zwei Datenbanken: `poweron_automation` vs `poweron_automation2` |
+| **I-3** | Zwei Scheduler: v1 (inkrementell, callback `automation.changed`) vs v2 (full wipe + re-register, callback `automation2.workflow.changed`) |
+| **I-4** | Zwei Execution Engines: `WorkflowManager` + `WorkflowProcessor` vs `executionEngine.executeGraph` |
+| **I-5** | Kein AI-Chat im Graphical Editor |
+| **I-6** | `WorkspaceInput` ist nicht als wiederverwendbare Komponente extrahiert |
+| **I-7** | UDB nur in Workspace und CommCoach, nicht im Graphical Editor |
+| **I-8** | AI Tools skalieren nicht: 50+ Tools ohne Gruppierungslogik, alle werden dem LLM gleichzeitig exponiert |
+
+---
+
+## 3. Ziel-Architektur
+
+### 3.1 Vier Säulen
+
+```
+┌──────────────────────────────────────────────────────────────────────┐
+│ POWERON UNIFIED PLATFORM │
+│ │
+│ ┌─────────────┐ ┌──────────────────────┐ ┌──────────────────┐ │
+│ │ AI Service │ │ Graphical Editor │ │ Automation │ │
+│ │ (Säule 1) │ │ (Säule 2) │ │ Scheduler │ │
+│ │ │ │ │ │ (Säule 3) │ │
+│ │ • AI Gateway │ │ • Visual Flow Builder │ │ │ │
+│ │ • Agent Loop │ │ • Node Palette │ │ • Cron Engine │ │
+│ │ • Toolboxes │ │ • AI Chat Sidebar │ │ • Event Trigger │ │
+│ │ • Sub-Agents │ │ • UDB Integration │ │ • Webhook Entry │ │
+│ │ • Streaming │ │ • Tracing/Debug Log │ │ • Run History │ │
+│ │ • Failover │ │ • Test Runner │ │ • Monitoring │ │
+│ │ • Neutraliz. │ │ • Version Management │ │ • Notifications │ │
+│ └──────┬───────┘ └──────────┬─────────────┘ └────────┬─────────┘ │
+│ │ │ │ │
+│ ┌──────▼──────────────────────▼───────────────────────────▼─────────┐ │
+│ │ SÄULE 4: UNIFIED ACTION LIBRARY (Toolboxes) │ │
+│ │ │ │
+│ │ Toolbox "ai" │ Toolbox "email" │ Toolbox "files" │ │
+│ │ ───────────── │ ────────────── │ ────────────── │ │
+│ │ ai.process │ outlook.readEmails │ context.extract │ │
+│ │ ai.webResearch │ outlook.searchEmails │ context.neutralize │ │
+│ │ ai.summarize │ outlook.draftEmail │ file.create │ │
+│ │ ai.translate │ outlook.sendDraft │ context.transform │ │
+│ │ ai.generate │ │ │ │
+│ │ │ │ │ │
+│ │ Toolbox "sharepoint"│ Toolbox "clickup" │ Toolbox "jira" │ │
+│ │ ─────────────────── │ ────────────── │ ───────────── │ │
+│ │ sharepoint.findDoc │ clickup.searchTasks │ jira.connect │ │
+│ │ sharepoint.readDocs │ clickup.createTask │ jira.exportTickets │ │
+│ │ sharepoint.upload │ clickup.updateTask │ jira.importTickets │ │
+│ │ ... │ ... │ ... │ │
+│ │ │ │
+│ │ Toolbox "trustee" │ Toolbox "chatbot" │ [future toolboxes] │ │
+│ │ ────────────────── │ ────────────── │ │ │
+│ │ trustee.extract │ chatbot.queryDB │ slack.sendMessage │ │
+│ │ trustee.process │ │ teams.postChannel │ │
+│ │ trustee.syncAcctng │ │ google.readDrive │ │
+│ └───────────────────────────────────────────────────────────────────┘ │
+└──────────────────────────────────────────────────────────────────────┘
+```
+
+### 3.2 Säule 1: AI Service (besteht, zu erweitern um Toolbox-Architektur)
+
+**Status:** Kern bereit (`serviceAi` + `serviceAgent` + `aicore`).
+
+**Erweiterung: Toolbox-Architektur für 50–100+ Tools** → siehe Abschnitt 5.
+
+### 3.3 Säule 2: Graphical Editor (Feature "graphicalEditor")
+
+**Status:** Basis vorhanden (Automation2FlowEditor), zu erweitern.
+
+**Funktionalität:**
+1. **Visual Flow Builder** (besteht): Canvas, Nodes, Connections, Branching, Loops
+2. **Node Palette** (besteht): Abgeleitet aus Toolbox-Registry — jede Toolbox-Action kann als Node exponiert werden
+3. **AI Chat Sidebar** (neu): Agent mit Toolbox `"workflow"` für Graph-Manipulation
+4. **UDB Integration** (neu): UnifiedDataBar im Editor
+5. **Tracing/Debug Log** (zu erweitern): RunStepLog mit Input-Snapshot, Duration, Error pro Node
+6. **Test Runner** (zu erweitern): Visual Highlighting, Step-by-Step, AI-assisted Debugging
+7. **Version Management** (neu): Draft/Published Lifecycle
+
+### 3.4 Säule 3: Automation Scheduler (zu konsolidieren)
+
+**Status:** v1-Scheduling hat bewährte Patterns, die direkt in v2 übernommen werden.
+
+**Von v1 übernehmen:**
+- Inkrementeller Sync (nur geänderte Jobs registrieren/entfernen, nicht full wipe)
+- Persistierter Job-Handle (`eventId`) auf dem Workflow für Debugging
+- `automation.changed` Callback-Pattern für reaktive Synchronisation
+- Handler lädt Workflow neu und prüft `active` vor Execution
+- Execution-Logs als Audit Trail (capped auf 50 Einträge)
+- Creator-User Tracking via `sysCreatedBy` für Execution-Kontext
+
+**Von v2 beibehalten:**
+- `IntervalTrigger` für einfache Wiederholungen
+- Main-Loop-Bridging (`call_soon_threadsafe`) für Thread-Safety
+- Delayed Startup Sync (5s) für DB-Readiness
+- Striktes Cron-Parsing (5/6 Felder)
+
+### 3.5 Säule 4: Unified Action Library mit Toolboxes
+
+**Status:** Infrastruktur besteht (`workflows/methods/` + `methodDiscovery` + `ActionExecutor`). Zu erweitern um Toolbox-Konzept.
+
+→ Detailliert in Abschnitt 5.
+
+---
+
+## 4. Mandant/Feature/RBAC-Architektur
+
+### 4.1 Hierarchie
+
+```
+System (PowerOn Platform)
+│
+├── System Template Rollen (isSystemRole=true, mandateId=null, featureCode=null)
+│ "admin", "user", "viewer"
+│ → kopiert bei Mandant-Erstellung via copySystemRolesToMandate()
+│
+├── Feature Template Rollen (isSystemRole=false, mandateId=null, featureCode=X)
+│ "workspace-admin", "workspace-user", "workspace-viewer"
+│ "graphicalEditor-admin", "graphicalEditor-user", ...
+│ → kopiert bei Feature-Instanz-Erstellung via _copyTemplateRoles()
+│
+├── Mandant (Tenant)
+│ ├── Mandate-Rollen (kopiert von System Templates)
+│ │ mandateId=X, featureInstanceId=null, featureCode=null
+│ │ "admin", "user", "viewer"
+│ │ └── UserMandateRole ← UserMandate ← User
+│ │
+│ ├── Feature-Instanz A (z.B. "AI Workspace Produktion")
+│ │ ├── Feature-Instanz-Rollen (kopiert von Feature Templates)
+│ │ │ mandateId=X, featureInstanceId=Y, featureCode="workspace"
+│ │ │ "workspace-admin", "workspace-user", "workspace-viewer"
+│ │ │ └── FeatureAccessRole ← FeatureAccess ← User
+│ │ └── Daten (Workflows, Files, Chats, ...)
+│ │
+│ ├── Feature-Instanz B (z.B. "Graphical Editor Dev")
+│ │ ├── Feature-Instanz-Rollen (kopiert von Feature Templates)
+│ │ │ mandateId=X, featureInstanceId=Z, featureCode="graphicalEditor"
+│ │ │ "graphicalEditor-admin", "graphicalEditor-user", ...
+│ │ └── Daten (Workflows, Runs, Tasks, ...)
+│ │
+│ └── Feature-Instanz C (z.B. "Trustee Buchhaltung")
+│ ├── Feature-Instanz-Rollen
+│ └── Daten (Positionen, Belege, ...)
+│
+└── Feature Catalog (Global)
+ ├── Feature "workspace" (code, label, icon)
+ ├── Feature "graphicalEditor" (code, label, icon)
+ ├── Feature "trustee" (code, label, icon)
+ └── ...
+```
+
+### 4.2 Zwei getrennte Template-Rollen-Systeme
+
+Es gibt **zwei getrennte** Template-Systeme — diese sind **nicht** dieselben:
+
+**System Template Rollen (für Mandanten)**
+
+| Feld | Wert |
+|------|------|
+| `isSystemRole` | `true` |
+| `mandateId` | `null` |
+| `featureInstanceId` | `null` |
+| `featureCode` | `null` |
+| `roleLabel` | `"admin"`, `"user"`, `"viewer"` |
+| Kopier-Mechanismus | `copySystemRolesToMandate()` in `interfaceBootstrap.py` |
+| Ausgelöst bei | Mandant-Erstellung |
+| Ziel-Rolle | `mandateId=X`, `featureInstanceId=null`, `isSystemRole=false` + kopierte AccessRules |
+
+**Feature Template Rollen (für Feature-Instanzen)**
+
+| Feld | Wert |
+|------|------|
+| `isSystemRole` | `false` |
+| `mandateId` | `null` |
+| `featureInstanceId` | `null` |
+| `featureCode` | z.B. `"workspace"`, `"automation2"` |
+| `roleLabel` | z.B. `"workspace-admin"`, `"workspace-user"`, `"workspace-viewer"` |
+| Kopier-Mechanismus | `_copyTemplateRoles()` in `interfaceFeatures.py` |
+| Ausgelöst bei | Feature-Instanz-Erstellung |
+| Ziel-Rolle | `mandateId=X`, `featureInstanceId=Y`, `isSystemRole=false` + kopierte AccessRules |
+
+**Wichtig:** Templates werden **nur bei Erstellung** kopiert. Spätere Änderungen werden **nicht** automatisch propagiert. (Ausnahme: `_syncTemplateRolesToDb()` synct neue UI-Objekte auf bestehende Mandate-Rollen mit gleichem `roleLabel`.)
+
+### 4.3 RBAC-Resolution (Priority-System)
+
+| Rollen-Typ | Scope | RBAC Priority |
+|------------|-------|---------------|
+| **Global/Template** | `mandateId=null, featureInstanceId=null` | 1 (niedrigste) |
+| **Mandate-Rolle** | `mandateId=X, featureInstanceId=null` | 2 |
+| **Instanz-Rolle** | `mandateId=X, featureInstanceId=Y` | 3 (höchste) |
+
+**Resolution:**
+1. Lade Mandate-Rollen (via `UserMandate` → `UserMandateRole`)
+2. Lade Feature-Instanz-Rollen (via `FeatureAccess` → `FeatureAccessRole`)
+3. Lade `AccessRules` für alle gefundenen Rollen
+4. **Höchste Priorität gewinnt** bei DATA-Permissions (Instanz schlägt Mandant schlägt Global)
+5. **View wird OR-verknüpft** über alle Rollen der höchsten Prioritätsstufe
+6. **Item-Spezifität** innerhalb einer Stufe: exact > prefix > generic
+
+### 4.3 AccessRule-Kontexte
+
+| Kontext | Item-Beispiel | Prüft |
+|---------|---------------|-------|
+| `UI` | `ui.feature.graphicalEditor.editor` | Seitenleiste, Navigation, Buttons |
+| `RESOURCE` | `resource.feature.graphicalEditor.execute` | Ausführungsberechtigungen |
+| `DATA` | Auto-generiert aus Tabellenname + featureCode | CRUD auf Datenbank-Ebene |
+
+### 4.4 Feature-Container-Pattern
+
+Jedes Feature folgt einem einheitlichen Container-Pattern:
+
+```
+features//
+├── main.py ◄── Feature-Definition
+│ ├── FEATURE_CODE = ""
+│ ├── UI_OBJECTS = [...] ◄── RBAC UI-Objekte
+│ ├── RESOURCE_OBJECTS = [...] ◄── RBAC Resource-Objekte
+│ ├── TEMPLATE_ROLES = [...] ◄── Rollen-Templates mit AccessRules
+│ ├── REQUIRED_SERVICES = [...] ◄── Service-Dependencies
+│ ├── registerFeature(catalog) ◄── Registriert Objekte + synct Rollen
+│ └── getFeatureDefinition() ◄── Katalog-Metadaten
+│
+├── routeFeature.py ◄── FastAPI Router (HTTP)
+│ └── router = APIRouter(prefix="/api/")
+│
+├── interfaceFeature.py ◄── DB-Interface (CRUD + RBAC)
+│ └── getInterface(user, mandateId, featureInstanceId)
+│
+├── datamodelFeature.py ◄── Pydantic-Modelle
+│
+└── [optional] service/ ◄── Feature-spezifische Services
+ └── mainService.py
+```
+
+**Automatische Discovery:** `registry.py` scannt `features/*/routeFeature*.py` und `features/*/main*.py`. Routers werden automatisch gemountet, Features automatisch im Katalog registriert. Kein manuelles Wiring nötig.
+
+---
+
+## 5. AI-Tool-Architektur: Toolboxes und Sub-Agents
+
+### 5.1 Problem: Tool-Explosion
+
+Aktuell werden ~50 Tools dem LLM in einem einzigen Prompt exponiert. Bei 100+ Tools:
+- LLM-Context wird überladen → schlechtere Tool-Auswahl
+- Irrelevante Tools für den aktuellen Kontext → Confusion und Halluzinationen
+- Keine Feature-isolierte Tool-Bereitstellung
+
+### 5.2 Konzept: Toolboxes
+
+Eine **Toolbox** ist eine thematisch gebündelte Gruppe von Tools mit einem klaren Scope. Toolboxes ersetzen das bisherige flache `_CORE_ONLY_TOOLS`-Set.
+
+```
+Toolbox-Registry
+│
+├── Toolbox "core" (immer verfügbar)
+│ ├── readFile, listFiles, searchInFileContent
+│ ├── writeFile, deleteFile, renameFile, copyFile
+│ ├── listFolders, createFolder, deleteFolder
+│ ├── webSearch, readUrl
+│ └── detectLanguage, translateText
+│
+├── Toolbox "ai" (AI-Operationen)
+│ ├── summarizeContent, describeImage
+│ ├── generateImage, textToSpeech, speechToText
+│ ├── renderDocument, createChart, executeCode
+│ └── neutralizeData
+│
+├── Toolbox "datasources" (Externe Verbindungen)
+│ ├── listConnections, browseDataSource, searchDataSource
+│ ├── downloadFromDataSource, uploadToExternal
+│ └── browseContainer, readContentObjects, extractContainerItem
+│
+├── Toolbox "email" (Outlook-Integration)
+│ ├── sendMail
+│ └── outlook.readEmails, outlook.searchEmails, outlook.draftEmail
+│
+├── Toolbox "sharepoint" (SharePoint-Integration)
+│ └── sharepoint.findDoc, sharepoint.read, sharepoint.upload, ...
+│
+├── Toolbox "clickup" (ClickUp-Integration)
+│ └── clickup.searchTasks, clickup.createTask, ...
+│
+├── Toolbox "jira" (Jira-Integration)
+│ └── jira.connect, jira.exportTickets, ...
+│
+├── Toolbox "workflow" (Graph-Manipulation für Editor-Chat)
+│ ├── readWorkflowGraph, addNode, removeNode
+│ ├── connectNodes, setNodeParameter
+│ ├── listAvailableNodeTypes, validateGraph
+│ └── listWorkflowHistory, readWorkflowMessages
+│
+├── Toolbox "trustee" (Feature-spezifisch)
+│ └── trustee.extract, trustee.process, trustee.syncAccounting
+│
+└── Toolbox "chatbot" (Feature-spezifisch)
+ └── chatbot.queryDatabase
+```
+
+### 5.3 Toolbox-Bereitstellung: Initiales Set + dynamische Eskalation
+
+**Kern-Idee:** Der Agent startet mit einem kompakten, eingeschränkten Tool-Set. Er kennt aber den **Katalog aller verfügbaren Toolboxes**. Wenn er für eine Aufgabe Spezial-Tools braucht, kann er eine Toolbox **anfordern** — und bekommt sie in der nächsten Runde bereitgestellt.
+
+**Warum nicht alle Tools sofort?**
+- Bei 50–100+ Tools wird der LLM-Context überladen → schlechtere Tool-Auswahl
+- Irrelevante Tools führen zu Confusion und Halluzinationen
+- Die meisten Interaktionen brauchen nur 10–15 Core-Tools
+
+**Mechanismus: `requestToolbox` Meta-Tool**
+
+```
+Runde 1:
+ Tools: core (readFile, webSearch, ...) + requestToolbox
+ System-Prompt enthält: "Verfügbare Toolboxes: email, sharepoint, clickup,
+ jira, workflow, ai, datasources, trustee, ..."
+
+ User: "Lies meine letzten Emails und fasse sie zusammen"
+ LLM: → requestToolbox("email") [braucht Email-Tools]
+
+Runde 2:
+ Tools: core + email (sendMail, outlook_readEmails, outlook_searchEmails, ...)
+ + requestToolbox
+
+ LLM: → outlook_readEmails(connectionRef, folder="Inbox", limit=10)
+
+Runde 3:
+ LLM: → (Text-Antwort mit Zusammenfassung)
+ → COMPLETED
+```
+
+**`requestToolbox` Tool-Definition:**
+
+```python
+{
+ "name": "requestToolbox",
+ "description": "Request additional specialized tools for the current task. "
+ "Call this when you need capabilities beyond your current tools.",
+ "parameters": {
+ "type": "object",
+ "properties": {
+ "toolboxId": {
+ "type": "string",
+ "description": "ID of the toolbox to activate",
+ "enum": [...] # Dynamisch: nur verfügbare Toolboxes
+ },
+ "reason": {
+ "type": "string",
+ "description": "Why you need this toolbox"
+ }
+ },
+ "required": ["toolboxId"]
+ }
+}
+```
+
+**AgentConfig:**
+
+```python
+class AgentConfig(BaseModel):
+ maxRounds: int = 25
+ maxCostCHF: Optional[float] = None
+ initialToolboxes: List[str] = ["core"] # Initiales Set
+ availableToolboxes: List[str] = [] # Was angefordert werden darf
+ temperature: Optional[float] = None
+```
+
+**Konfiguration pro Feature:**
+
+| Feature | `initialToolboxes` | `availableToolboxes` (anforderbar) |
+|---------|--------------------|------------------------------------|
+| **Workspace** | `["core"]` | `["ai", "datasources", "email", "sharepoint", "clickup", "jira", "workflow"]` — gefiltert nach User-Connections |
+| **Graphical Editor Chat** | `["core", "workflow"]` | `["ai"]` |
+| **CommCoach** | `["core"]` | `[]` (keine Eskalation erlaubt) |
+| **Chatbot** | `["core"]` | `[]` |
+
+**Vorteile:**
+- **Lean Context:** Die meisten Chats brauchen nur Core-Tools → schnellere, präzisere Antworten
+- **Selbst-navigierend:** LLM entscheidet selbst, wann Spezial-Tools nötig sind
+- **Kontrolliert:** `availableToolboxes` limitiert, was der Agent anfordern darf (RBAC)
+- **Skaliert:** 100+ Tools kein Problem — der LLM sieht immer nur was er gerade braucht
+- **Transparent:** `requestToolbox("email", reason="User fragt nach Emails")` ist nachvollziehbar im Tracing
+
+**Automatische Toolbox-Verfügbarkeit basierend auf Connections:**
+Welche Toolboxes in `availableToolboxes` erscheinen, hängt von den aktiven User-Connections ab. Hat der User keine Outlook-Verbindung, erscheint `"email"` gar nicht in der Liste — der Agent kann sie auch nicht anfordern.
+
+### 5.4 Sub-Agents (Feature Data Agents)
+
+Für datenintensive Features mit eigenem Schema (z.B. Trustee Buchhaltung) existiert bereits das Sub-Agent-Pattern:
+
+```
+User Prompt im Workspace
+ │
+ ▼
+Main Agent (Toolboxes: core, ai, datasources, ...)
+ │
+ ├── Tool: queryFeatureInstance("trustee", instanceId, question)
+ │ │
+ │ ▼
+ │ Sub-Agent (Feature Data Agent)
+ │ Eigene ToolRegistry: browseTable, queryTable
+ │ Eigenes System-Prompt mit Schema-Wissen
+ │ Limitiert: maxRounds=5, maxCost=0.10 CHF
+ │ │
+ │ ▼
+ │ Antwort zurück an Main Agent
+ │
+ ├── Tool: readFile(fileId)
+ │ (Core Tool, direkt)
+ │
+ └── Tool: outlook_readEmails(connectionRef)
+ (Action Tool, direkt)
+```
+
+**Empfehlung: Sub-Agent-Pattern ausweiten auf:**
+- **Trustee** (besteht): browseTable, queryTable auf Buchhaltungsdaten
+- **Workflow** (neu): Graph-Manipulation als Sub-Agent im Editor-Chat
+- **Future Features** mit eigenem Datenmodell: Gleicher Pattern
+
+**Vorteil:** Der Main Agent bleibt schlank. Feature-spezifisches Datenwissen ist im Sub-Agent gekapselt. Der LLM des Main Agents sieht nur ein einzelnes Tool (`queryFeatureInstance`) statt 10+ tabellenspezifische Tools.
+
+### 5.5 State Machine: AI Agent Run (mit Toolbox-Eskalation)
+
+```
+ ┌──────────┐
+ │ IDLE │
+ └────┬─────┘
+ │ runAgent(prompt, initialToolboxes, availableToolboxes)
+ ▼
+ ┌──────────┐
+ ┌────►│ THINKING │◄──────────────────────────────────┐
+ │ └────┬─────┘ │
+ │ │ │
+ │ ├── LLM Response (Text only) ──────────────┼──► COMPLETED
+ │ │ │
+ │ └── LLM Response (ToolCalls) │
+ │ │ │
+ │ ▼ │
+ │ ┌────────────────────┐ │
+ │ │ EXECUTING_TOOLS │ │
+ │ └────┬───────────────┘ │
+ │ │ │
+ │ ├── requestToolbox(id) ───┐ │
+ │ │ ▼ │
+ │ │ ┌─────────────────────┐ │
+ │ │ │ TOOLBOX_ESCALATION │ │
+ │ │ │ Toolbox wird aktiviert│ │
+ │ │ │ Tools der nächsten │ │
+ │ │ │ Runde erweitert │ │
+ │ │ └──────────┬──────────┘ │
+ │ │ │ │
+ │ ├── Sub-Agent call ───┐ │ │
+ │ │ ▼ │ │
+ │ │ ┌──────────────┐│ │
+ │ │ │SUB_AGENT_CALL ││ │
+ │ │ └──────┬───────┘│ │
+ │ │ │ │ │
+ │ ├── Regular tools ─┼────────┼────────────────┘
+ │ │ (results → next round)
+ │
+ │ maxRounds / maxCost reached
+ ▼
+ ┌──────────┐
+ │ COMPLETED │ (oder FAILED / CANCELLED)
+ └──────────┘
+```
+
+---
+
+## 6. Use Cases
+
+### UC-1: Datenquellen einbinden → besteht, keine Änderung
+
+### UC-2: AI Workspace mit UDB → besteht, keine Änderung
+
+### UC-3: Workflow im Workspace erstellen/bearbeiten
+
+**Beschreibung:** User beschreibt im Workspace-Chat einen gewünschten Workflow → Agent nutzt Toolbox `"workflow"` → generiert Graph → speichert als Draft → User öffnet im Graphical Editor.
+
+**Voraussetzung:** Die `"workflow"` Toolbox muss im Workspace aktivierbar sein. Die Tools validieren jeden Graph-Mutationsschritt:
+- `addNode(type, parameters)` → prüft: existiert der Node-Typ? Sind die Parameter valide?
+- `connectNodes(source, target)` → prüft: sind die I/O-Typen kompatibel? Keine Zyklen?
+- `validateGraph()` → vollständige Validierung inkl. Trigger-Prüfung
+
+### UC-4: AI Chat im Graphical Editor
+
+**Beschreibung:** Der Graphical Editor hat eine Sidebar mit Chat. Der Agent nutzt Toolbox `"workflow"` mit speziellem System-Prompt, der den aktuellen Graph kennt. Er kann Nodes hinzufügen, entfernen, umkonfigurieren und den Graph erklären.
+
+**Architektur:** Gleicher `serviceAgent.runAgent()` Pfad wie Workspace, aber:
+- Anderes ToolSet: `toolboxes: ["core", "workflow"]`
+- System-Prompt enthält den aktuellen Graph als Kontext
+- Frontend: Extrahierte `ChatBar`-Komponente + `ChatStream`
+
+### UC-5: Workflow testen mit Tracing Log
+
+**Beschreibung:** User klickt "Test Run" → Graph wird ausgeführt → jeder Node wird visuell hervorgehoben (Status-Farben) → Tracing Log zeigt pro Node: Input, Output, Dauer, Fehler.
+
+**Bei Fehlern:** User bespricht den Fehler im AI Chat → Agent liest den RunStepLog und schlägt Korrekturen vor.
+
+**Technisch:**
+- `RunStepLog` pro Node mit Timestamps, Input-Snapshot, Duration, Error-Stack
+- SSE/WebSocket für Live-Updates des Node-Status
+- Run-Modes: "Full Run" und "Step-by-Step" (Pause nach jedem Node)
+
+### UC-6: Workflow automatisieren (Scheduler)
+
+**Beschreibung:** User konfiguriert Schedule im Editor (via trigger.schedule Node oder Invocation) → konsolidierter Scheduler registriert Cron-Job → Run-History wird persistiert → bei Fehlern Notifications.
+
+---
+
+## 7. State Machines
+
+### 7.1 Workflow Lifecycle
+
+```
+ ┌───────┐
+ create ──►│ DRAFT │◄──── edit (neuer graph)
+ └───┬───┘
+ │ publish
+ ▼
+ ┌───────────┐
+ unpublish│ PUBLISHED │◄──── edit → erzeugt neuen Draft,
+ │ └───────────┘ Published bleibt aktiv
+ │ │
+ │ │ archive (manuell oder nach Deaktivierung)
+ │ ▼
+ │ ┌───────────┐
+ │ │ ARCHIVED │
+ │ └───────────┘
+ │ │
+ └───────┘ re-publish (aus Archiv zurückholen)
+```
+
+**Regeln:**
+- Ein Workflow hat genau **eine** published Version (oder keine)
+- Scheduled Runs nutzen immer die published Version
+- Edit erzeugt immer einen neuen Draft — die published Version bleibt stabil
+- Archive ist reversibel
+
+### 7.2 WorkflowRun Lifecycle
+
+```
+ ┌─────────┐
+ executeGraph──►│ RUNNING │
+ └────┬────┘
+ │
+ ┌────────────────┼────────────────┐
+ │ │ │
+ ▼ ▼ ▼
+ ┌─────────┐ ┌──────────┐ ┌────────┐
+ │ PAUSED │ │COMPLETED │ │ FAILED │
+ └────┬────┘ └──────────┘ └────────┘
+ │
+ │ resume (human task complete / email received)
+ │
+ ▼
+ ┌─────────┐
+ │ RUNNING │ (Weiter ab pausiertem Node)
+ └────┬────┘
+ │
+ ├──► COMPLETED
+ ├──► FAILED
+ ├──► PAUSED (nächster Input-Node)
+ └──► CANCELLED (manuell abgebrochen)
+```
+
+**Pause-Gründe:**
+- `input.*` Node → HumanTask erstellt → wartet auf User-Eingabe
+- `email.checkEmail` → wartet auf neue Email (Background Poller)
+
+### 7.3 HumanTask Lifecycle
+
+```
+ ┌─────────┐
+ │ PENDING │ (erstellt bei Pause)
+ └────┬────┘
+ │
+ ├── User completes → COMPLETED (Run wird resumed)
+ ├── User cancels → CANCELLED (Run wird cancelled)
+ └── Timeout → EXPIRED (Run failsafe: cancel oder default)
+```
+
+### 7.4 Scheduler Job Lifecycle
+
+```
+ ┌─────────────┐
+ │ UNREGISTERED │ (Workflow exists, kein Schedule)
+ └──────┬──────┘
+ │ activate + publish mit schedule invocation
+ ▼
+ ┌─────────────┐
+ │ REGISTERED │ (APScheduler Job aktiv, eventId gesetzt)
+ └──────┬──────┘
+ │
+ ├── Cron fires → executeGraph → WorkflowRun
+ ├── Workflow deactivated → UNREGISTERED (Job entfernt, eventId=null)
+ ├── Schedule geändert → Job replaced (replaceExisting=true)
+ └── Workflow gelöscht → UNREGISTERED (Job entfernt)
+```
+
+---
+
+## 8. Empfehlungen und offene Punkte
+
+### 8.1 Starke Konzepte (umsetzen)
+
+| Konzept | Bewertung |
+|---------|-----------|
+| **Toolbox-Architektur** | Skaliert von 50 auf 100+ Tools ohne LLM-Überlastung. Klare Isolierung pro Thema. |
+| **Sub-Agents pro Feature** | Bewährtes Pattern (Trustee), erweiterbar. Hält Main Agent schlank. |
+| **Validierungs-Layer für Workflow-Modellierung** | Graph-Mutation nur über validierte Tools. LLM kann keinen inkonsistenten Graph erzeugen. |
+| **v1-Scheduling-Patterns in v2 übernehmen** | Inkrementeller Sync ist robuster als Full-Wipe. Execution-Logs sind essentiell für Audit. |
+| **State Machines als Grundlage** | Klare Zustände, klare Übergänge. Erweiterbar ohne Breaking Changes. |
+
+### 8.2 Was User brauchen, um das als zentrales Arbeitsinstrument zu nutzen
+
+| User Need | Feature | Priorität |
+|-----------|---------|-----------|
+| **Schneller Einstieg** | Workflow-Templates für häufige Use Cases (z.B. "Email-Attachment → AI Analyse → SharePoint Upload") | Hoch |
+| **Vertrauen** | Tracing Log + Test Runner — User muss sehen, was der Workflow tut | Hoch |
+| **Effizienz** | AI-gestützte Workflow-Erstellung per Chat | Hoch |
+| **Fehlertoleranz** | Retry-Policies, Pause/Resume, klare Fehlermeldungen | Hoch |
+| **Kontext** | UDB im Editor — Zugriff auf Files/Sources bei der Konfiguration | Hoch |
+| **Zusammenarbeit** | Workflows teilen im Mandant, Rollen-basierter Zugriff | Mittel |
+| **Monitoring** | Dashboard: laufende Automationen, nächste Runs, Fehlerrate, Kosten | Mittel |
+| **Flexibilität** | Custom Script Nodes (Python-Sandbox), AI Decision Nodes | Mittel |
+| **Mobile** | Notifications + Approval-Tasks per Mobile | Phase 2 |
+
+### 8.3 Offene Architektur-Fragen
+
+| Frage | Empfehlung |
+|-------|------------|
+| **Toolbox-Selektion: statisch oder dynamisch?** | Hybrid: Basis-Toolboxes pro Feature statisch konfiguriert, Connection-basierte Toolboxes dynamisch aktiviert. |
+| **Sub-Agent Tiefe: verschachtelt?** | Maximal 1 Level tief. Main Agent → Sub-Agent. Kein Sub-Sub-Agent (zu komplex, unkontrollierbar). |
+| **Graph-Execution und WorkflowManager: zusammenführen?** | Nein, getrennt lassen. `executeGraph` für den Graphical Editor, `WorkflowManager` bleibt für den dynamischen Workspace-Modus. Verschiedene Orchestrierungsmodelle. |
+| **Feature-Code: `automation2` → `graphicalEditor`?** | Ja, Rename in Phase 1. Klarer Name für User und Entwickler. |
+
+---
+
+## 9. Phasen-Plan
+
+### Phase 1: Foundation
+- Unified Workflow Datenmodell (Workflow + WorkflowVersion + WorkflowRun + RunStepLog + HumanTask)
+- Toolbox-Registry implementieren (Ablösung `_CORE_ONLY_TOOLS`)
+- ChatBar-Komponente extrahieren
+- Feature Rename: `automation2` → `graphicalEditor`
+- Scheduler konsolidieren (v1 Patterns in v2 Engine)
+
+### Phase 2: Editor Enhancement
+- AI Chat Sidebar im Editor (Toolbox `"workflow"` + Graph-Manipulation-Tools)
+- UDB Integration im Editor
+- WorkflowVersion Lifecycle (Draft/Published)
+- Enhanced RunStepLog + Visual Tracing
+
+### Phase 3: Productization
+- Workflow Templates / Marketplace
+- Monitoring Dashboard
+- Retry Policies pro Node
+- Notifications bei Scheduler-Fehlern
+- Automation v1 Feature entfernen (wird nicht mehr benötigt)
+
+### Phase 4: Advanced
+- AI Decision Node (`ai.decide`)
+- Custom Script Node (Python Sandbox)
+- Dynamische Toolbox-Aktivierung basierend auf Connections
+- Sub-Agent Pattern für weitere Features
+
+---
+
+## 10. Glossar
+
+| Begriff | Definition |
+|---------|-----------|
+| **Toolbox** | Thematisch gebündelte Gruppe von AI-Tools, die kontextabhängig aktiviert werden |
+| **Sub-Agent** | Spezialisierter Mini-Agent mit eigener Tool-Registry, aufgerufen vom Main Agent |
+| **Workflow** | Persistiertes Automatisierungsmodell mit Graph-Struktur |
+| **WorkflowVersion** | Immutable Snapshot eines Workflow-Graphen (draft, published, archived) |
+| **WorkflowRun** | Einzelne Ausführung einer WorkflowVersion |
+| **RunStepLog** | Detaillierter Log pro Node-Execution innerhalb eines Runs |
+| **HumanTask** | Aufgabe für menschliche Eingabe, erstellt bei Pause eines Runs |
+| **Node** | Ausführungsschritt im Graph (Trigger, Action, Flow-Control, Input/Human) |
+| **Method** | Integrations-Kategorie (z.B. methodOutlook) mit mehreren Actions |
+| **Action** | Spezifische Operation innerhalb einer Method (z.B. outlook.readEmails) |
+| **Invocation** | Entry-Point für einen Workflow (Manual, Schedule, Webhook, Form, Event) |
+| **UDB** | Unified Data Bar — Multi-Tab-Panel für Chats, Files und Sources |
+| **ChatBar** | Wiederverwendbare Prompt-Input-Komponente |
+| **Feature Container** | Einheitliches Verzeichnis-Pattern für ein Feature (main, route, interface, datamodel) |
diff --git a/concepts/Automation Data Model.md b/concepts/Automation Data Model.md
new file mode 100644
index 0000000..5dd5c32
--- /dev/null
+++ b/concepts/Automation Data Model.md
@@ -0,0 +1,994 @@
+# PowerOn Automation Unification — Datenmodell & Architektur
+
+**Version:** 2.0
+**Datum:** 2026-04-05
+**Referenz:** [Automation Business Spec.md](./Automation%20Business%20Spec.md)
+
+---
+
+## 1. Plattform-Architektur: Schichten
+
+```
+┌──────────────────────────────────────────────────────────────────┐
+│ FRONTEND (React/TS) │
+│ │
+│ Shared Components: ChatBar, ChatStream, UnifiedDataBar, │
+│ FlowEditor, FormGenerator, FolderTree │
+│ │
+│ Feature Pages: WorkspacePage, GraphicalEditorPage, │
+│ CommcoachDossierView, ChatbotView, ... │
+└──────────────────────────────┬────────────────────────────────────┘
+ │ HTTP/SSE/WebSocket
+┌──────────────────────────────▼────────────────────────────────────┐
+│ GATEWAY (FastAPI) │
+│ │
+│ ┌─────────────────────────────────────────────────────────────┐ │
+│ │ Layer 1: FEATURES (Feature-Instanz-gebunden) │ │
+│ │ │ │
+│ │ features/workspace/ features/graphicalEditor/ │ │
+│ │ features/commcoach/ features/chatbot/ │ │
+│ │ features/trustee/ features/realEstate/ │ │
+│ │ features/neutralization/ features/teamsbot/ │ │
+│ └──────────────────────────┬──────────────────────────────────┘ │
+│ │ nutzt │
+│ ┌──────────────────────────▼──────────────────────────────────┐ │
+│ │ Layer 2: SERVICES (Feature-unabhängig, pro Request) │ │
+│ │ │ │
+│ │ serviceAgent serviceAi serviceKnowledge serviceBilling │ │
+│ │ serviceChat serviceExtraction serviceGeneration │ │
+│ │ serviceMessaging serviceWeb serviceSubscription │ │
+│ └──────────────────────────┬──────────────────────────────────┘ │
+│ │ nutzt │
+│ ┌──────────────────────────▼──────────────────────────────────┐ │
+│ │ Layer 3: SHARED INFRASTRUCTURE │ │
+│ │ │ │
+│ │ workflows/methods/ (Unified Action Library / Toolboxes)│ │
+│ │ workflows/processing/ (ActionExecutor, methodDiscovery) │ │
+│ │ workflows/automation2/ (Graph Execution Engine) │ │
+│ │ interfaces/ (DB-Abstraktion + RBAC) │ │
+│ │ aicore/ (Provider Plugins + Model Selector) │ │
+│ │ datamodels/ (Pydantic Models) │ │
+│ │ security/ (RBAC Engine) │ │
+│ │ shared/ (Utilities, eventManagement) │ │
+│ └─────────────────────────────────────────────────────────────┘ │
+└───────────────────────────────────────────────────────────────────┘
+```
+
+---
+
+## 2. Feature-Container-Pattern (Ist-Zustand, bewährt)
+
+### 2.1 Verzeichnisstruktur
+
+```
+features//
+├── main.py ◄── Feature-Definition + Registrierung
+│ FEATURE_CODE: str Feature-Identifikator
+│ UI_OBJECTS: List[Dict] RBAC-fähige UI-Objekte
+│ RESOURCE_OBJECTS: List[Dict] RBAC-fähige Resource-Objekte
+│ TEMPLATE_ROLES: List[Dict] Rollen-Templates mit AccessRules
+│ REQUIRED_SERVICES: List[Dict] Service-Dependencies (optional)
+│ registerFeature(catalog) → bool Registriert Objekte, synct Rollen in DB
+│ getFeatureDefinition() → Dict Katalog-Metadaten (Label, Icon)
+│ getServices(user, ...) → Hub Service-Hub-Factory (optional)
+│
+├── routeFeature.py ◄── HTTP API
+│ router = APIRouter(prefix="/api/")
+│ [templateRouter] = APIRouter(...) Zusätzliche Router (optional)
+│
+├── interfaceFeature.py ◄── Datenbankzugriff
+│ Objects CRUD + RBAC-Filterung
+│ getInterface(user, mandateId, featureInstanceId) → Singleton
+│
+├── datamodelFeature.py ◄── Pydantic-Datenmodelle
+│ class (PowerOnModel): ... Feature-spezifische Entitäten
+│
+├── nodeDefinitions/ (optional) ◄── Graph-Node-Typen (für graphicalEditor)
+│ triggers.py, flow.py, ai.py, ...
+│
+└── service/ (optional) ◄── Feature-interne Services
+ └── mainService.py
+```
+
+### 2.2 Automatische Discovery
+
+```python
+# system/registry.py — Kein manuelles Wiring nötig
+
+discoverFeatureContainers()
+ # Scannt features/*/routeFeature*.py → Liste von Feature-Dirs
+
+loadFeatureRouters(app)
+ # Importiert routeFeature*.py, mountet router + *Router
+
+loadFeatureMainModules()
+ # Importiert main*.py, cached
+
+registerAllFeaturesInCatalog(catalogService)
+ # Für jedes main-Module:
+ # getFeatureDefinition() → catalogService.registerFeatureDefinition()
+ # registerFeature() → UI/Resource-Objekte + Template-Rollen sync
+```
+
+### 2.3 Service-Hub-Wiring
+
+```python
+# serviceHub/__init__.py — Dynamisches Service-Wiring
+
+class ServiceHub:
+ def __init__(self, user, workflow, mandateId, featureInstanceId):
+ # ServiceCenterContext erstellen
+ self._serviceCenterContext = ServiceCenterContext(...)
+
+ # Core Interfaces laden
+ self.interfaceDbApp = getAppInterface(user, mandateId)
+ self.interfaceDbChat = getChatInterface(user, mandateId, featureInstanceId)
+
+ # Feature-Interfaces dynamisch laden (glob features/*/interfaceFeature*.py)
+ self._loadFeatureInterfaces()
+
+ # Feature-Services dynamisch laden (glob features/*/service*/mainService*.py)
+ self._loadFeatureServices()
+
+ def __getattr__(self, name):
+ # Lazy: shared services via getService(name, context) bei Erstzugriff
+```
+
+---
+
+## 3. RBAC-Architektur
+
+### 3.1 Zwei getrennte Template-Rollen-Systeme
+
+**WICHTIG:** Es gibt zwei getrennte Template-Systeme — diese sind **nicht** dieselben:
+
+```
+┌────────────────────────────────────────────────────────────────────┐
+│ TEMPLATE-ROLLEN (Global) │
+│ │
+│ ┌──────────────────────────────────────────────────────────┐ │
+│ │ System Templates (für Mandanten) Priority 1 │ │
+│ │ isSystemRole=true, mandateId=null, featureCode=null │ │
+│ │ │ │
+│ │ "admin" → Mandant-Administration │ │
+│ │ "user" → Standard-User │ │
+│ │ "viewer" → Nur-Lesen │ │
+│ │ │ │
+│ │ → kopiert bei Mandant-Erstellung (copySystemRolesToMandate)│ │
+│ └──────────────────────────────────────────────────────────┘ │
+│ │
+│ ┌──────────────────────────────────────────────────────────┐ │
+│ │ Feature Templates (für Feature-Instanzen) Priority 1 │ │
+│ │ isSystemRole=false, mandateId=null, featureCode="workspace"│ │
+│ │ │ │
+│ │ "workspace-admin" → alle UI/RESOURCE/DATA Rechte │ │
+│ │ "workspace-user" → eingeschränkte DATA Rechte │ │
+│ │ "workspace-viewer" → nur View │ │
+│ │ │ │
+│ │ → kopiert bei Feature-Instanz-Erstellung (_copyTemplateRoles)│ │
+│ └──────────────────────────────────────────────────────────┘ │
+└────────────────────────────────────────────────────────────────────┘
+
+┌────────────────────────────────────────────────────────────────────┐
+│ MANDATE-ROLLEN Priority 2 │
+│ mandateId=X, featureInstanceId=null, featureCode=null │
+│ │
+│ "admin" (kopiert von System Template) │
+│ "user" (kopiert von System Template) │
+│ "viewer" (kopiert von System Template) │
+│ → zugewiesen via UserMandate → UserMandateRole │
+└────────────────────────────────────────────────────────────────────┘
+
+┌────────────────────────────────────────────────────────────────────┐
+│ INSTANZ-ROLLEN Priority 3 │
+│ mandateId=X, featureInstanceId=Y, featureCode="workspace" │
+│ │
+│ "workspace-admin" (kopiert von Feature Template) │
+│ "workspace-user" (kopiert von Feature Template) │
+│ "workspace-viewer" (kopiert von Feature Template) │
+│ → zugewiesen via FeatureAccess → FeatureAccessRole │
+└────────────────────────────────────────────────────────────────────┘
+```
+
+### 3.2 Datenmodell RBAC
+
+```python
+class Role(PowerOnModel):
+ # Scope bestimmt die Priorität:
+ mandateId: Optional[str] # null = Global/Template
+ featureInstanceId: Optional[str] # null = nicht Instanz-spezifisch
+ featureCode: Optional[str] # z.B. "workspace", "graphicalEditor"
+ roleLabel: str # z.B. "workspace-admin"
+ isSystemRole: bool # System-Rollen nicht löschbar
+
+class AccessRule(PowerOnModel):
+ roleId: str # FK → Role
+ context: AccessRuleContext # DATA | UI | RESOURCE
+ item: Optional[str] # null=generic, oder spezifisch
+ view: bool
+ read: Optional[AccessLevel] # "n" | "o" | "m" | "a"
+ create: Optional[AccessLevel]
+ update: Optional[AccessLevel]
+ delete: Optional[AccessLevel]
+
+class AccessLevel(str, Enum):
+ NONE = "n" # kein Zugriff
+ OWN = "o" # nur eigene Records
+ MANDATE = "m" # alle Records im Mandant
+ ALL = "a" # alle Records (System-Admin)
+```
+
+### 3.3 User-Zuweisung
+
+```
+User ──► UserMandate ──► UserMandateRole ──► Role (Mandate-Scope)
+User ──► FeatureAccess ──► FeatureAccessRole ──► Role (Instanz-Scope)
+```
+
+```python
+class UserMandate(PowerOnModel):
+ userId: str
+ mandateId: str
+
+class UserMandateRole(PowerOnModel):
+ userMandateId: str # FK → UserMandate
+ roleId: str # FK → Role (mandateId=X, featureInstanceId=null)
+
+class FeatureAccess(PowerOnModel):
+ userId: str
+ featureInstanceId: str
+ enabled: bool
+
+class FeatureAccessRole(PowerOnModel):
+ featureAccessId: str # FK → FeatureAccess
+ roleId: str # FK → Role (mandateId=X, featureInstanceId=Y)
+```
+
+### 3.4 Resolution-Algorithmus
+
+```
+1. Sammle roleIds:
+ - Mandate-Rollen: UserMandate → UserMandateRole → roleId
+ - Instanz-Rollen: FeatureAccess → FeatureAccessRole → roleId
+
+2. Lade AccessRules für alle roleIds (context + item filter)
+
+3. Bestimme Priorität pro Rolle:
+ - Priority 3: role.featureInstanceId gesetzt (Instanz)
+ - Priority 2: role.mandateId gesetzt (Mandant)
+ - Priority 1: beides null (Global/Template)
+
+4. DATA-Permissions: Nur Rules der HÖCHSTEN Priorität zählen
+ View: OR über alle Rules der höchsten Priorität
+ Item-Spezifität: exact > prefix > generic (innerhalb Priorität)
+
+5. SQL WHERE Clause: buildRbacWhereClause(permissions, user, table)
+```
+
+---
+
+## 4. Unified Workflow Datenmodell
+
+### 4.1 Entitäten
+
+```python
+# Ziel: datamodels/datamodelWorkflowUnified.py oder
+# features/graphicalEditor/datamodelFeatureGraphicalEditor.py
+
+class WorkflowStatus(str, Enum):
+ DRAFT = "draft"
+ PUBLISHED = "published"
+ ARCHIVED = "archived"
+
+class Workflow(PowerOnModel):
+ """Workflow-Metadaten und Ownership."""
+ id: str
+ mandateId: str
+ featureInstanceId: str
+ label: str
+ description: Optional[str]
+ tags: List[str] = []
+ isTemplate: bool = False
+ templateSourceId: Optional[str] # geklont von diesem Template
+ currentVersionId: Optional[str] # aktive/published Version
+ active: bool = True # Scheduler-enabled
+ eventId: Optional[str] # APScheduler Job-ID (v1-Pattern)
+
+class WorkflowVersion(PowerOnModel):
+ """Immutable Snapshot eines Workflow-Graphen."""
+ id: str
+ workflowId: str # FK → Workflow
+ versionNumber: int # auto-increment
+ status: WorkflowStatus # draft | published | archived
+ graph: Dict[str, Any] # { nodes: [...], connections: [...] }
+ invocations: List[Dict[str, Any]] # Entry-Points (manual, schedule, webhook, form)
+ publishedAt: Optional[float]
+ publishedBy: Optional[str]
+```
+
+### 4.2 State Machine: WorkflowVersion.status
+
+```
+ ┌───────┐
+ create ───►│ DRAFT │◄─── edit (graph mutation via API oder AI Tools)
+ └───┬───┘
+ │ publish
+ ▼
+ ┌───────────┐
+ unpublish─►│ PUBLISHED │
+ (→ DRAFT) └─────┬─────┘
+ │ archive
+ ▼
+ ┌───────────┐
+ │ ARCHIVED │──► re-publish (→ PUBLISHED)
+ └───────────┘
+
+Invariante: Pro Workflow maximal 1 Version mit status=PUBLISHED.
+ Scheduler nutzt immer die PUBLISHED Version.
+```
+
+### 4.3 Run-Modell
+
+```python
+class RunStatus(str, Enum):
+ PENDING = "pending"
+ RUNNING = "running"
+ PAUSED = "paused"
+ COMPLETED = "completed"
+ FAILED = "failed"
+ CANCELLED = "cancelled"
+
+class WorkflowRun(PowerOnModel):
+ """Einzelne Ausführung einer WorkflowVersion."""
+ id: str
+ workflowId: str # FK → Workflow
+ versionId: str # FK → WorkflowVersion
+ status: RunStatus
+ trigger: Dict[str, Any] # { type: "manual"|"schedule"|"webhook"|..., metadata }
+ startedAt: float
+ completedAt: Optional[float]
+ nodeOutputs: Dict[str, Any] # Outputs pro Node-ID
+ currentNodeId: Optional[str] # Paused bei diesem Node
+ resumeContext: Dict[str, Any] # Kontext für Resume
+ error: Optional[str] # Top-Level Fehler
+ costTokens: Optional[int] # Aggregierte Token-Kosten
+ costCredits: Optional[float] # Aggregierte Credit-Kosten
+```
+
+### 4.4 State Machine: WorkflowRun.status
+
+```
+ ┌─────────┐
+ executeGraph──►│ RUNNING │
+ └────┬────┘
+ │
+ ┌────────────────┼────────────────┐
+ │ │ │
+ ▼ ▼ ▼
+┌────────┐ ┌──────────┐ ┌────────┐
+│ PAUSED │ │COMPLETED │ │ FAILED │
+└───┬────┘ └──────────┘ └────────┘
+ │
+ │ resume(taskResult | emailReceived)
+ ▼
+┌─────────┐
+│ RUNNING │───► COMPLETED | FAILED | PAUSED | CANCELLED
+└─────────┘
+
+Pause-Gründe:
+ - input.* Node → HumanTask erstellt
+ - email.checkEmail → EmailWait (Background Poller)
+
+Cancel:
+ - Manuell durch User
+ - Timeout bei HumanTask (expiresAt)
+```
+
+### 4.5 RunStepLog und HumanTask
+
+```python
+class StepStatus(str, Enum):
+ RUNNING = "running"
+ COMPLETED = "completed"
+ FAILED = "failed"
+ SKIPPED = "skipped"
+
+class RunStepLog(PowerOnModel):
+ """Detaillierter Log pro Node-Execution."""
+ id: str
+ runId: str # FK → WorkflowRun
+ nodeId: str # Node-ID im Graph
+ nodeType: str # z.B. "ai.prompt", "email.checkEmail"
+ status: StepStatus
+ inputSnapshot: Dict[str, Any] # Parameters + Upstream-Daten bei Execution
+ output: Optional[Dict[str, Any]] # Node-Output
+ error: Optional[str]
+ startedAt: float
+ completedAt: Optional[float]
+ durationMs: Optional[int]
+ tokensUsed: Optional[int] # AI-Calls in diesem Step
+ retryCount: int = 0
+
+class TaskStatus(str, Enum):
+ PENDING = "pending"
+ COMPLETED = "completed"
+ CANCELLED = "cancelled"
+ EXPIRED = "expired"
+
+class HumanTask(PowerOnModel):
+ """Aufgabe für menschliche Eingabe bei Pause."""
+ id: str
+ runId: str # FK → WorkflowRun
+ workflowId: str # FK → Workflow (Convenience-FK)
+ nodeId: str
+ nodeType: str # input.approval, input.form, etc.
+ config: Dict[str, Any] # Node-Parameter (Formular-Felder, Approval-Text)
+ assigneeId: str
+ status: TaskStatus
+ result: Optional[Dict[str, Any]]
+ expiresAt: Optional[float] # Timeout
+```
+
+### 4.6 State Machine: HumanTask.status
+
+```
+┌─────────┐
+│ PENDING │ (erstellt bei Run-Pause)
+└────┬────┘
+ │
+ ├── complete(result) → COMPLETED (Run wird resumed mit result)
+ ├── cancel() → CANCELLED (Run wird cancelled)
+ └── expiresAt passed → EXPIRED (Run wird cancelled oder Default)
+```
+
+### 4.7 ER-Diagramm
+
+```
+┌──────────────────┐ ┌──────────────────────┐
+│ Workflow │ 1───N │ WorkflowVersion │
+├──────────────────┤ ├──────────────────────┤
+│ id │ │ id │
+│ mandateId │ │ workflowId (FK) │
+│ featureInstanceId│ │ versionNumber │
+│ label │ │ status │
+│ description │ │ graph {} │
+│ tags [] │ │ invocations [] │
+│ isTemplate │ │ publishedAt │
+│ currentVersionId ├───1──►│ publishedBy │
+│ active │ └──────────┬───────────┘
+│ eventId │ │ 1:N
+└──────────────────┘ ┌──────────▼───────────┐
+ │ WorkflowRun │
+ ├──────────────────────┤
+ │ id │
+ │ workflowId (FK) │
+ │ versionId (FK) │
+ │ status │
+ │ trigger {} │
+ │ startedAt │
+ │ completedAt │
+ │ nodeOutputs {} │
+ │ currentNodeId │
+ │ resumeContext {} │
+ │ error │
+ │ costTokens │
+ │ costCredits │
+ └──────────┬───────────┘
+ 1:N │ │ 1:N
+ ┌─────────────┘ └──────────┐
+ ▼ ▼
+ ┌──────────────────┐ ┌──────────────────┐
+ │ RunStepLog │ │ HumanTask │
+ ├──────────────────┤ ├──────────────────┤
+ │ id │ │ id │
+ │ runId (FK) │ │ runId (FK) │
+ │ nodeId │ │ workflowId (FK) │
+ │ nodeType │ │ nodeId │
+ │ status │ │ nodeType │
+ │ inputSnapshot {} │ │ config {} │
+ │ output {} │ │ assigneeId │
+ │ error │ │ status │
+ │ startedAt │ │ result {} │
+ │ completedAt │ │ expiresAt │
+ │ durationMs │ └──────────────────┘
+ │ tokensUsed │
+ │ retryCount │
+ └──────────────────┘
+```
+
+---
+
+## 5. AI-Tool-Architektur: Toolbox-Datenmodell
+
+### 5.1 Toolbox-Definition
+
+```python
+# Ziel: serviceCenter/services/serviceAgent/toolboxRegistry.py
+
+class ToolboxDefinition(BaseModel):
+ """Definition einer thematischen Tool-Gruppe."""
+ id: str # z.B. "core", "email", "sharepoint", "workflow"
+ label: Dict[str, str] # Multilingual: {"en": "Email", "de": "E-Mail"}
+ description: str
+ featureCode: Optional[str] # null = Feature-unabhängig, "trustee" = Feature-spezifisch
+ tools: List[str] # Tool-Namen in dieser Toolbox
+ isDefault: bool = False # Immer aktiv (z.B. "core")
+ requiresConnection: Optional[str] # "outlook", "sharepoint" → auto-aktiviert bei Connection
+
+class ToolboxRegistry:
+ """Verwaltet Toolboxes und deren Zuordnung zu Tools."""
+ _toolboxes: Dict[str, ToolboxDefinition]
+ _toolRegistry: ToolRegistry # Referenz auf die bestehende ToolRegistry
+
+ def registerToolbox(self, toolbox: ToolboxDefinition): ...
+
+ def getActiveToolboxes(
+ self,
+ featureCode: str,
+ userConnections: List[str], # Aktive Connection-Typen des Users
+ explicitToolboxes: List[str], # Explizit aktivierte Toolboxes
+ ) -> List[str]: ...
+
+ def getToolsForToolboxes(
+ self,
+ activeToolboxIds: List[str],
+ ) -> List[ToolDefinition]: ...
+```
+
+### 5.2 AgentConfig (erweitert mit Eskalation)
+
+```python
+class AgentConfig(BaseModel):
+ maxRounds: int = 25
+ maxCostCHF: Optional[float] = None
+ initialToolboxes: List[str] = ["core"] # Tools bei Runde 1
+ availableToolboxes: List[str] = [] # Via requestToolbox anforderbar
+ temperature: Optional[float] = None
+```
+
+### 5.3 Dynamische Toolbox-Eskalation
+
+**Kern-Idee:** Der Agent startet mit einem kompakten Initial-Set (`initialToolboxes`). Er kennt den Katalog aller `availableToolboxes`. Wenn er Spezial-Tools braucht, ruft er `requestToolbox(id)` auf — die Tools werden in der nächsten Runde bereitgestellt.
+
+```
+Runde 1:
+ Aktive Tools: core (readFile, webSearch, ...) + requestToolbox
+ System-Prompt: "Verfügbare Toolboxes: email, sharepoint, ai, ..."
+
+ User: "Lies meine letzten Emails und fasse sie zusammen"
+ LLM: → requestToolbox("email")
+
+Runde 2:
+ Aktive Tools: core + email (outlook_readEmails, ...) + requestToolbox
+ LLM: → outlook_readEmails(connectionRef, folder="Inbox")
+
+Runde 3:
+ LLM: → (Text-Antwort mit Zusammenfassung) → COMPLETED
+```
+
+**requestToolbox Meta-Tool:**
+
+```python
+{
+ "name": "requestToolbox",
+ "description": "Request specialized tools for the current task.",
+ "parameters": {
+ "toolboxId": {
+ "type": "string",
+ "enum": [...] # Dynamisch: nur availableToolboxes
+ },
+ "reason": {"type": "string"}
+ }
+}
+```
+
+### 5.4 Toolbox-Registrierung (Architektur)
+
+```
+App Startup
+ │
+ ├── _registerCoreTools(registry) → 40 Core-Tools in ToolRegistry
+ ├── ActionToolAdapter.registerAll(registry) → dynamicMode Actions als Tools
+ │
+ └── ToolboxRegistry.registerAll()
+ ├── Toolbox "core" → readFile, listFiles, webSearch, ... (isDefault=true)
+ ├── Toolbox "ai" → summarizeContent, generateImage, ...
+ ├── Toolbox "datasources" → browseDataSource, downloadFromDataSource, ...
+ ├── Toolbox "email" → sendMail, outlook_readEmails, ... (requiresConnection="outlook")
+ ├── Toolbox "sharepoint" → sharepoint_findDocumentPath, ... (requiresConnection="sharepoint")
+ ├── Toolbox "clickup" → clickup_searchTasks, ... (requiresConnection="clickup")
+ ├── Toolbox "jira" → jira_connectJira, ... (requiresConnection="jira")
+ ├── Toolbox "workflow" → readWorkflowGraph, addNode, connectNodes, ...
+ ├── Toolbox "trustee" → trustee_extractFromFiles, ... (featureCode="trustee")
+ └── Toolbox "chatbot" → chatbot_queryDatabase, ... (featureCode="chatbot")
+
+Per Agent-Run:
+ │
+ ├── Feature-Instanz Config → initialToolboxes + availableToolboxes
+ ├── User Connections → filtert availableToolboxes (nur was User nutzen kann)
+ │
+ ├── Runde 1: getToolsForToolboxes(initialToolboxes) + requestToolbox
+ │
+ └── Nach requestToolbox("email"):
+ Runde 2+: getToolsForToolboxes(initialToolboxes + ["email"]) + requestToolbox
+```
+
+### 5.5 Sub-Agent-Architektur
+
+```python
+# Bestehendes Pattern: featureDataAgent.py
+
+# Aufruf-Kette:
+# Main Agent → Tool "queryFeatureInstance" → _queryFeatureInstance()
+# → runFeatureDataAgent(question, featureInstanceId, ...)
+# → Eigene ToolRegistry (browseTable, queryTable)
+# → runAgentLoop(prompt, registry, config=AgentConfig(maxRounds=5))
+# → Antwort zurück → ToolResult → Main Agent
+```
+
+**Sub-Agents sind komplementär zu Toolboxes:**
+- **Toolbox-Eskalation:** Agent bekommt Tools direkt und nutzt sie selbst (z.B. Email-Tools)
+- **Sub-Agent:** Agent delegiert an spezialisierten Mini-Agent mit eigenem Wissen (z.B. Trustee DB-Schema)
+
+Faustregel: Toolbox wenn die Tools generisch sind. Sub-Agent wenn Feature-spezifisches Kontext-/Schema-Wissen nötig ist.
+
+### 5.6 Toolbox-Zuordnung pro Feature
+
+| Feature | `initialToolboxes` | `availableToolboxes` (anforderbar) |
+|---------|--------------------|------------------------------------|
+| **Workspace** | `["core"]` | `["ai", "datasources", "email", "sharepoint", "clickup", "jira"]` — gefiltert nach User-Connections |
+| **Graphical Editor Chat** | `["core", "workflow"]` | `["ai"]` |
+| **CommCoach** | `["core"]` | `[]` (keine Eskalation) |
+| **Chatbot** | (Eigenes Tool-System) | — |
+
+---
+
+## 6. Graph-Struktur und Node-Definitionen
+
+### 6.1 Graph-Schema (innerhalb WorkflowVersion.graph)
+
+```json
+{
+ "nodes": [
+ {
+ "id": "node-uuid",
+ "type": "trigger.schedule",
+ "position": { "x": 100, "y": 200 },
+ "parameters": {
+ "cronExpression": "0 8 * * 1-5"
+ }
+ },
+ {
+ "id": "node-uuid-2",
+ "type": "ai.prompt",
+ "position": { "x": 300, "y": 200 },
+ "parameters": {
+ "prompt": "Analysiere die Emails und erstelle eine Zusammenfassung"
+ }
+ }
+ ],
+ "connections": [
+ {
+ "source": "node-uuid",
+ "target": "node-uuid-2",
+ "sourceOutput": 0,
+ "targetInput": 0
+ }
+ ]
+}
+```
+
+### 6.2 Node-Definition-Schema
+
+```python
+# Jede Node-Definition ist ein Dict mit folgender Struktur:
+{
+ "id": "ai.prompt", # Unique Node-Type-ID
+ "category": "ai", # Kategorie (für Palette-Gruppierung)
+ "label": {"en": "Prompt", "de": "Prompt"}, # Multilingual
+ "description": {"en": "...", "de": "..."},
+ "parameters": [
+ {
+ "name": "prompt",
+ "type": "string",
+ "required": True,
+ "description": {"en": "AI prompt", "de": "KI-Prompt"}
+ }
+ ],
+ "inputs": 1, # Anzahl Eingänge
+ "outputs": 1, # Anzahl Ausgänge
+ "meta": {"icon": "mdi-robot", "color": "#9C27B0"},
+
+ # Interne Felder (nicht an Frontend exponiert):
+ "_method": "ai", # Method für ActionExecutor
+ "_action": "process", # Action für ActionExecutor
+ "_paramMap": {"prompt": "aiPrompt"}, # Parameter-Mapping Node → Action
+}
+```
+
+### 6.3 Node-Type zu Method/Action Mapping
+
+| Node Type | `_method` | `_action` | `_paramMap` |
+|-----------|-----------|-----------|-------------|
+| `trigger.manual` | — | — | — (TriggerExecutor) |
+| `trigger.schedule` | — | — | — (TriggerExecutor) |
+| `trigger.form` | — | — | — (TriggerExecutor) |
+| `flow.ifElse` | — | — | — (FlowExecutor) |
+| `flow.switch` | — | — | — (FlowExecutor) |
+| `flow.loop` | — | — | — (FlowExecutor) |
+| `input.*` | — | — | — (InputExecutor → HumanTask) |
+| `ai.prompt` | `ai` | `process` | `prompt→aiPrompt` |
+| `ai.webResearch` | `ai` | `webResearch` | `query→prompt` |
+| `ai.summarizeDocument` | `ai` | `summarizeDocument` | — |
+| `ai.translateDocument` | `ai` | `translateDocument` | `targetLanguage→targetLanguage` |
+| `ai.generateDocument` | `ai` | `generateDocument` | `prompt→prompt` |
+| `email.checkEmail` | `outlook` | `readEmails` | `connectionId→connectionReference` |
+| `email.searchEmail` | `outlook` | `searchEmails` | `connectionId→connectionReference` |
+| `email.draftEmail` | `outlook` | `composeAndDraft...` | `connectionId→connectionReference` |
+| `sharepoint.*` | `sharepoint` | entsprechend | `connectionId→connectionReference` |
+| `clickup.*` | `clickup` | entsprechend | `connectionId→connectionReference` |
+| `file.create` | `file` | `create` | `template→template` |
+
+### 6.4 Execution-Routing
+
+```
+executeGraph(graph, services, ...)
+ │
+ ├── parseGraph() → nodes, connections, nodeIds
+ ├── validateGraph() → Konsistenzprüfung
+ ├── topoSort(nodes, connectionMap) → geordnete Node-Liste
+ │
+ └── Pro Node:
+ │
+ ├── nodeType.startsWith("trigger.") → TriggerExecutor
+ │ → runEnvelope passthrough
+ │
+ ├── nodeType.startsWith("flow.") → FlowExecutor
+ │ → ifElse: evaluiert Bedingung, setzt active path
+ │ → switch: evaluiert Match, setzt active path
+ │ → loop: iteriert mit _loopState
+ │
+ ├── nodeType.startsWith("input.") → InputExecutor
+ │ → erstellt HumanTask
+ │ → setzt Run auf PAUSED
+ │ → raise PauseForHumanTaskError
+ │
+ └── nodeType.startsWith("ai." | "email." | "sharepoint." | ...) → ActionNodeExecutor
+ │
+ ├── _getNodeDefinition(nodeType) → _method, _action, _paramMap
+ ├── Parameter-Mapping (Node-Params → Action-Params)
+ ├── Upstream-Daten mergen (documents von vorherigen Nodes)
+ └── ActionExecutor.executeAction(method, action, params)
+```
+
+---
+
+## 7. Scheduler-Architektur (konsolidiert)
+
+### 7.1 Konsolidierter Scheduler (Ziel)
+
+```python
+# Ziel: workflows/scheduler/mainScheduler.py
+# Übernimmt die besten Patterns aus v1 und v2
+
+class WorkflowScheduler:
+ """Konsolidierter Scheduler für Workflow-Automationen."""
+
+ def start(self, eventUser):
+ """Startup: Initial Sync + Delayed Sync + Callback Registration."""
+ eventManager.start()
+ self._syncScheduledWorkflows(eventUser)
+ self._registerDelayedSync(eventUser, delaySeconds=5)
+ callbackRegistry.register("workflow.changed",
+ lambda _: self._syncScheduledWorkflows(eventUser))
+
+ def stop(self, eventUser):
+ """Shutdown: Cleanup."""
+ pass # APScheduler handles job cleanup
+
+ def _syncScheduledWorkflows(self, eventUser):
+ """Inkrementeller Sync (v1-Pattern)."""
+ workflows = self._getAllSchedulableWorkflows()
+
+ for wf in workflows:
+ jobId = f"workflow.{wf.id}"
+
+ if wf.active and wf.currentVersionId:
+ # Register/Replace Job
+ version = self._getPublishedVersion(wf.currentVersionId)
+ cronKwargs = self._extractSchedule(version)
+
+ if cronKwargs:
+ handler = self._createHandler(wf, version, eventUser)
+ eventManager.registerCron(
+ jobId=jobId,
+ func=handler,
+ cronKwargs=cronKwargs,
+ replaceExisting=True # v1-Pattern: atomic replace
+ )
+ # Persist eventId on Workflow (v1-Pattern: debugging)
+ if wf.eventId != jobId:
+ self._updateEventId(wf.id, jobId)
+ else:
+ # Deactivated: Remove Job + Clear eventId
+ self._removeJob(jobId)
+ if wf.eventId:
+ self._updateEventId(wf.id, None)
+
+ def _createHandler(self, wf, version, eventUser):
+ """Handler: Reload + Active-Check + Execute (v1-Pattern)."""
+ async def handler():
+ # Reload Workflow (v1-Pattern: Zustand könnte sich geändert haben)
+ current = self._getWorkflow(wf.id)
+ if not current or not current.active:
+ return
+
+ # Execute published version
+ services = self._buildServices(current, eventUser)
+ result = await executeGraph(
+ graph=version.graph,
+ services=services,
+ workflowId=current.id,
+ ...
+ )
+
+ # Execution Log (v1-Pattern: capped audit trail)
+ self._appendExecutionLog(current.id, result)
+
+ # Thread-Bridge (v2-Pattern)
+ return self._wrapAsync(handler)
+```
+
+### 7.2 Scheduler State Machine
+
+```
+Workflow.active=false ──► UNREGISTERED (kein APScheduler Job)
+ │
+ │ activate + published version mit schedule invocation
+ ▼
+Workflow.active=true ──► REGISTERED (eventId gesetzt, Job aktiv)
+ │
+ ├── Cron fires → handler() → executeGraph → WorkflowRun
+ │
+ ├── Workflow deactivated → remove job → UNREGISTERED (eventId=null)
+ │
+ ├── Schedule geändert → replaceExisting=true → REGISTERED (neuer Trigger)
+ │
+ └── Workflow gelöscht → remove job → (Workflow gelöscht)
+```
+
+---
+
+## 8. Backend Code-Struktur (Ziel)
+
+```
+gateway/modules/
+│
+├── features/
+│ ├── graphicalEditor/ ◄── NEUES FEATURE (ersetzt automation2)
+│ │ ├── mainGraphicalEditor.py
+│ │ │ FEATURE_CODE = "graphicalEditor"
+│ │ │ UI_OBJECTS = [
+│ │ │ "ui.feature.graphicalEditor.editor",
+│ │ │ "ui.feature.graphicalEditor.workflows",
+│ │ │ "ui.feature.graphicalEditor.tasks",
+│ │ │ ]
+│ │ │ RESOURCE_OBJECTS = [
+│ │ │ "resource.feature.graphicalEditor.execute",
+│ │ │ "resource.feature.graphicalEditor.schedule",
+│ │ │ ]
+│ │ │ TEMPLATE_ROLES = [
+│ │ │ "graphicalEditor-admin",
+│ │ │ "graphicalEditor-user",
+│ │ │ "graphicalEditor-viewer",
+│ │ │ ]
+│ │ │
+│ │ ├── routeFeatureGraphicalEditor.py
+│ │ │ router prefix: /api/workflows
+│ │ │ Endpoints: CRUD Workflows, Versions, Execute, Runs, Tasks, NodeTypes, Chat
+│ │ │
+│ │ ├── interfaceFeatureGraphicalEditor.py
+│ │ │ DB: poweron_workflows (oder poweron_automation2 renamed)
+│ │ │ Tables: Workflow, WorkflowVersion, WorkflowRun, RunStepLog, HumanTask
+│ │ │
+│ │ ├── datamodelFeatureGraphicalEditor.py
+│ │ │ Workflow, WorkflowVersion, WorkflowRun, RunStepLog, HumanTask
+│ │ │ WorkflowStatus, RunStatus, StepStatus, TaskStatus (State Machines)
+│ │ │
+│ │ └── nodeDefinitions/
+│ │ triggers.py, flow.py, input.py, ai.py, email.py, sharepoint.py, clickup.py, file.py
+│ │
+│ ├── workspace/ ◄── BLEIBT (AI Chat mit UDB)
+│ ├── automation/ ◄── BLEIBT bis Ablösung durch graphicalEditor
+│ ├── commcoach/ ◄── BLEIBT
+│ ├── chatbot/ ◄── BLEIBT
+│ ├── trustee/ ◄── BLEIBT
+│ └── ...
+│
+├── serviceCenter/services/
+│ ├── serviceAgent/
+│ │ ├── mainServiceAgent.py ◄── Core Tools + Toolbox-Tagging
+│ │ ├── agentLoop.py ◄── Toolbox-Filtering statt toolSet
+│ │ ├── toolRegistry.py ◄── BLEIBT
+│ │ ├── toolboxRegistry.py ◄── NEU: Toolbox-Verwaltung
+│ │ ├── actionToolAdapter.py ◄── BLEIBT (+ Toolbox-Zuordnung)
+│ │ ├── featureDataAgent.py ◄── BLEIBT (Sub-Agent Pattern)
+│ │ └── datamodelAgent.py ◄── toolboxes: List[str] statt toolSet
+│ └── ...
+│
+├── workflows/
+│ ├── methods/ ◄── BLEIBT (Unified Action Library)
+│ ├── processing/ ◄── BLEIBT (ActionExecutor, methodDiscovery)
+│ ├── automation2/ ◄── Graph Engine BLEIBT (execution, executors, graphUtils)
+│ ├── scheduler/ ◄── NEU (konsolidierter Scheduler)
+│ │ └── mainScheduler.py
+│ ├── automation/ ◄── BLEIBT bis Ablösung
+│ └── workflowManager.py ◄── BLEIBT (für Workspace Dynamic Mode)
+│
+└── ...
+```
+
+---
+
+## 9. Frontend Code-Struktur (Ziel)
+
+```
+frontend_nyla/src/
+│
+├── components/ ◄── SHARED COMPONENTS
+│ ├── ChatBar/ ◄── NEU (extrahiert aus WorkspaceInput)
+│ │ ├── ChatBar.tsx Props: onSend, onStop, onVoice,
+│ │ │ fileAttachments, showProviderSelector,
+│ │ │ showVoice, placeholder
+│ │ ├── ChatBar.module.css
+│ │ └── index.ts
+│ │
+│ ├── ChatStream/ ◄── NEU (extrahiert aus workspace/ChatStream)
+│ │ ├── ChatStream.tsx SSE-driven message stream, wiederverwendbar
+│ │ └── index.ts
+│ │
+│ ├── UnifiedDataBar/ ◄── BLEIBT
+│ │
+│ ├── FlowEditor/ ◄── RENAMED von Automation2FlowEditor
+│ │ ├── editor/
+│ │ │ ├── FlowEditor.tsx + AI Chat Panel (ChatBar + ChatStream)
+│ │ │ ├── FlowCanvas.tsx + Visual Run Tracing
+│ │ │ ├── EditorChatPanel.tsx ◄── NEU
+│ │ │ └── ...
+│ │ └── ...
+│ │
+│ └── ...
+│
+├── pages/views/
+│ ├── workspace/
+│ │ ├── WorkspacePage.tsx Nutzt ChatBar, ChatStream, UDB
+│ │ └── ...
+│ │
+│ ├── graphicalEditor/ ◄── RENAMED von automation2
+│ │ ├── GraphicalEditorPage.tsx FlowEditor + UDB + ChatPanel
+│ │ ├── WorkflowsListPage.tsx
+│ │ └── TasksPage.tsx
+│ │
+│ └── ...
+│
+├── api/
+│ ├── workflowApi.ts ◄── Konsolidiert (ersetzt automationApi + automation2Api)
+│ └── ...
+│
+└── ...
+```
+
+---
+
+## 10. Zusammenfassung: Architektur-Prinzipien
+
+| Prinzip | Umsetzung |
+|---------|-----------|
+| **Feature-Container-Pattern** | Jedes Feature folgt dem gleichen Verzeichnis-Pattern mit automatischer Discovery. Kein manuelles Wiring. |
+| **RBAC auf zwei Ebenen** | Mandate-Rollen (generell) + Instanz-Rollen (feature-spezifisch). Höchste Priorität gewinnt. |
+| **State Machines als Grundlage** | WorkflowVersion, WorkflowRun, HumanTask und Scheduler haben klar definierte Zustände und Übergänge. |
+| **Unified Action Library** | Eine Method/Action-Library für alle Consumers (Agent Tools, Graph Nodes, Workflow Processor). |
+| **Toolboxes statt flache Tool-Liste** | Thematisch gruppierte Tools, kontextabhängig aktiviert. Skaliert auf 100+ Tools. |
+| **Sub-Agents für Feature-Daten** | Main Agent bleibt schlank. Feature-spezifisches Wissen in dedizierten Sub-Agents. |
+| **Keine Migration** | Direkte Implementierung auf Automation2-Basis. v1 bleibt bis Ablösung. |
+| **Scheduler konsolidiert** | Beste Patterns aus v1 (inkrementell, eventId, reload+check) und v2 (interval, thread-bridge). |