# WorkflowAutomation als System-Komponente — `graphicalEditor` raus aus dem Feature-Modell > **Treiber:** Entscheid aus `c-work/0-ideas/2026-06-CustomerCases-step1-architecture.md` / `…-step3-features-plan.md` **A0.4**. Dort wurde die Entkopplung als Roadmap markiert; sie wird **vorgezogen**, weil sie sonst die Solution-Schicht (Ownership/RBAC/Billing) später erneut einholt. > > **Namens-Konvention:** Die System-Komponente heisst **`WorkflowAutomation`** (Code-Token `WorkflowAutomation`/`workflowAutomation`) — semantisch eindeutig und gut greppbar. Das generische Wort «Automation» ist im Bestand schon mehrfach belegt (`AutomationsDashboardPage`, `routeAutomationWorkspace`, `/automations`, `workflows/automation2`) und bleibt dort **unverändert**. UI-Label (kundensichtbar): **«Workflow-Automation»** / «Automatisierung». ## Beschreibung und Kontext `graphicalEditor` ist heute ein **Hybrid**: formal ein Feature (Discovery, `TEMPLATE_ROLES` `graphicalEditor-*`, Store-Eintrag, `FeatureInstance` pro Mandant, per-Instanz-API `/api/workflows/{instanceId}/…`), architektonisch aber **mandanten- und feature-übergreifende Orchestrierungs-Infrastruktur**: - **Kein `getDataObjects()`** — keine Domänen-/Kundendaten; `AutoWorkflow`/`AutoRun` sind Orchestrierungs-Metadaten (eigene DB `poweron_graphicaleditor`). - **Einziges Feature mit `onStart`/`onStop`** — bootet beim App-Start den **globalen** Scheduler (Email-Poller startet **on-demand** via `emailPoller.ensureRunning`, gestoppt in `onStop`). - **Scheduler/Engine liegen ohnehin unter `modules/workflows/`** (System), nicht im Feature. - **`getWorkflows()` filtert nur nach `mandateId`** («cross-instance»); ein Graph referenziert legitim mehrere Features/Instanzen über per-Node `FeatureInstanceRef`. - **Cross-Mandate-Ops-Sicht existiert bereits**: `AutomationsDashboardPage` (`/automations`, Tabs Workflows/Runs/Details) ← `routeWorkflowDashboard.py` (`/api/system/workflow-runs`), RBAC über Mandats-Mitgliedschaft + Platform-Admin — **ganz ohne** GE-FeatureInstance. **Business-Treiber:** Die Solution-Schicht (CustomerCases) verankert Ownership an **Mandant + Run-as-Principal** (A0.1). Solange `graphicalEditor` als Feature mit per-Instanz-RBAC/Billing geführt wird, kollidiert jede Solution mit einem erfundenen «Host-Owner». Wir lösen die Wurzel: **WorkflowAutomation = System-Komponente** mit eigener Navigations-Gruppe «Workflow-Automation» (mehrere Tab-Seiten, mandanten-/feature-übergreifend strukturiert an einem Ort) und sauberem Daten-/RBAC-Modell. **Risiko, wenn NICHT gemacht:** doppelte Wahrheit (Feature-Instanz-RBAC vs. mandatsweite Realität), Billing fix auf `graphicalEditor` statt auf berührte Instanz, per-Instanz-URLs (`{instanceId}`) zementieren das Feature-Modell, und die Solution-Schicht erbt den Designfehler. ## Fokus und kritische Details - **Kein Big-Bang.** Parallel-Pfade bauen (mandatsweite API neben `{instanceId}`-API), dann umschalten, dann Feature-Mantel entfernen. Jede Phase ist für sich lauffähig. - **Scheduler-Boot ist fragil.** Er hängt am Feature-`onStart` (`app.py`-Lifespan iteriert alle Feature-Module). Vor dem Entfernen des Feature-Mantels **muss** der Boot in einen System-Lifespan-Hook umziehen, sonst startet der Scheduler nicht mehr. - **`featureInstanceId` hat heute zwei Bedeutungen:** (a) GE-Owner/RBAC-Scope auf `AutoWorkflow`, (b) Daten-Scope der Ausführung (`targetFeatureInstanceId` + per-Node `FeatureInstanceRef`). Nur (a) wird abgebaut; (b) bleibt — es ist die Cross-Feature-Referenz. - **Kaum DDL nötig (Code-verifiziert).** Tabellen werden **ohne `NOT NULL`** (ausser `id`-PK) und **ohne DB-FKs** angelegt (`connectorDbPostgre.py` `_create_table_from_model`); FKs sind app-level (`fkRegistry`, `app.py` `validateFkTargets`). Heisst: `featureInstanceId` ist **schon jetzt DB-nullable** → «nullable machen» ist nur `Optional[str]` im Pydantic-Modell (`datamodelFeatureGraphicalEditor.py`) + Query-/Code-Anpassungen, **keine Migration**. `runAsPrincipal` = additives `ADD COLUMN` (von `_ensureTableExists` abgedeckt). Echte Arbeit = Application-Logik (Routen/Queries, die `featureInstanceId` voraussetzen), nicht Schema. - **Bestehende Daten grandfathern.** `AutoWorkflow`-Zeilen tragen `featureInstanceId`; `FeatureAccess`-Grants und per-Instanz-Rollen existieren. Datenmigration als **einmaliges Skript** (Muster: `scripts/script_migrate_feature_instance_refs.py`; Cascade-Vorbild: `routeWorkflowDashboard._cascadeDeleteAutoWorkflow`). Migration muss diese erhalten/überführen, nicht löschen. - **RBAC-Präzedenz nutzen — aber sie deckt nur Lesen.** `routeWorkflowDashboard.py` (`_scopedWorkflowFilter`/`_getAdminMandateIds` + `isPlatformAdmin`) ist ein **Read/List/Delete**-Muster — **kein** Create/Update/Publish/Execute. Die **39** `_validateInstanceAccess`-Sites in `routeFeatureGraphicalEditor.py` sind grösstenteils Write/Execute; dafür braucht es einen **eigens entworfenen** Helper `_validateWorkflowAccess(workflow, context, action)` (read vs. write), nicht 1:1 das Dashboard-Muster. `system`-Pseudo-Feature (`instantiable=False`) ist die Vorlage für Katalog-/RBAC-Registrierung ohne Instanzen. - **Shared Contracts statt Boundary-Leck (Code-verifiziert).** `graphicalEditor/portTypes` (`PORT_TYPE_CATALOG`) und der Node-Katalog (`STATIC_NODE_TYPES`) werden vom **geteilten** Layer + Chats genutzt (`methods/methodBase.py`, `processing/shared/parameterValidation.py`, `serviceAgent/actionToolAdapter.py`, `shared/i18nRegistry.py`) — sie dürfen **nicht** in die Komponente, sonst importiert Shared/Chat *aufwärts*. Zudem liegen zwei Helfer (`PauseForHumanTaskError`, `_coerce_document_data_to_bytes`) in `automation2.executors`, werden aber von `methods/` importiert → **vor** dem Engine-Umzug in den Shared-Layer **delaminieren**. - **Zwei UI-Oberflächen sauber trennen** (siehe «Verhältnis zur Solution-Schicht»): die **WorkflowAutomation-Gruppe** (technische Orchestrierung, cross-mandate, für Power-User/Admins) vs. die kundenseitige **«Lösungen»-Surface** pro Host-Feature (bleibt eingebettet, Präsentation). ## Ziel und Nicht-Ziele - **Ziel:** `graphicalEditor` als System-Komponente **`WorkflowAutomation`** führen — eigene Top-Level-Navigationsgruppe mit Tab-Seiten (Editor · Vorlagen · Workflows · Läufe · Tasks), mandanten-/feature-übergreifend. - **Ziel:** Mandatsweite API + RBAC (kein `{instanceId}` als RBAC-Anker), Scheduler-Boot als System-Lifespan, DB-Scoping auf **Mandant + Principal** (statt GE-Owner-Instanz). - **Ziel:** Bestehende Workflows/Runs/Tasks und Grants verlustfrei migrieren. - **Ziel:** Fundament legen für die `Solution`-Schicht (A0.1) — Solutions hängen an Mandant + Run-as-Principal, nicht an einer GE-Instanz. - **Explizit NICHT:** Graph-Format/Node-Semantik ändern (`modules/workflows/automation2/*` bleibt funktional gleich). **Ausnahme:** per-Node-Billing-Attribution (AC5) ist eine bewusste, lokal begrenzte Executor-Änderung (berührte `FeatureInstanceRef` in den `recordUsage`-Kontext threaden) — kein Graph-/Semantik-Umbau. - **Explizit NICHT:** den geteilten **Workflow-Execution-Layer** (`workflows/methods/`, `workflows/processing/`) in die Komponente ziehen — er bleibt unter `modules/workflows/` (Chats/Agents nutzen ihn **ohne** Automation). - **Explizit NICHT:** die kundenseitige «Lösungen»-Surface in diesem Plan bauen (→ CustomerCases-Pläne; dieser Plan liefert nur das Substrat). - **Explizit NICHT:** per-Node `FeatureInstanceRef` / `targetFeatureInstanceId` (Daten-Scope) abschaffen. - **Explizit NICHT:** DB-Engine/Datenbankname wechseln (`poweron_graphicaleditor` bleibt). - **Explizit NICHT:** bestehende «Automation»-Code-Namen umbenennen (`AutomationsDashboardPage`, `routeAutomationWorkspace`, `workflows/automation2`) — nur die *neue* Komponente trägt das Token `WorkflowAutomation`. ## Zielarchitektur (Kurz) ``` VORHER (Feature): Nav: Mandant → graphicalEditor-Instanz → {editor, templates, tasks} API: /api/workflows/{geInstanceId}/... RBAC: FeatureAccess auf GE-Instanz DB : AutoWorkflow.featureInstanceId = GE-Owner (RBAC-Scope) Boot: graphicalEditor.onStart → Scheduler NACHHER (System-Komponente WorkflowAutomation): Nav: Top-Level-Gruppe «Workflow-Automation» (einmal) → Tabs {Editor, Vorlagen, Workflows, Läufe, Tasks} + Mandanten-/Instanz-Picker in der Seite (cross-mandate) API: /api/workflow-automation/... RBAC: Mandats-Mitgliedschaft + System-Rollen + isPlatformAdmin DB : AutoWorkflow.mandateId + runAsPrincipal (Owner); featureInstanceId → entfällt als RBAC-Anker targetFeatureInstanceId + per-Node FeatureInstanceRef bleiben (Daten-Scope) Boot: System-Lifespan (app.py) → Scheduler ``` ## Namensgebung & modulare Struktur **Name der System-Komponente: `WorkflowAutomation`** (UI-Label «Workflow-Automation» / «Automatisierung»). `graphicalEditor` ist **nicht** der Name der Komponente, sondern **ein Modul** darin (der Graph-/Flow-Editor). Begründung für `WorkflowAutomation`: semantisch eindeutig + als Token greppbar (das generische «Automation» ist im Code mehrfach belegt und bleibt). (Verworfen: «Automation» allein — mehrdeutig; «Orchestration» — neuer Begriff ohne Code-Verankerung.) Die Komponente bündelt die bestehenden, heute verstreuten **Automation**-Teile (`features/graphicalEditor/`, `workflows/automation2/`, `workflows/scheduler/`) **plus** die kommende L3/L4-Schicht zu **einem nachvollziehbaren, modularen System**. Den geteilten **Workflow-Execution-Layer** (`workflows/methods/`, `workflows/processing/`) bündelt sie **nicht** — der bleibt eigenständig (siehe Abgrenzung unten). Jedes Modul hat eine klare Verantwortung und mappt auf die L2–L4-Schichten der Architektur. | L | Modul | Verantwortung | |---|---|---| | L2 | **editor** (= heute `graphicalEditor` Authoring) | Graph/Flow-Authoring, Node-Registry, Adapter, Editor-UI-Backend (konsumiert Shared Contracts) | | L2 | **engine** (= heute `workflows/automation2/`) | Graph-Ausführungs-Runtime (Graph → Run) | | L2 | **scheduler** | Zeit-/Event-Trigger, Email-Poller | | L3 | **solutions** | konfigurierte Workflows (Solution-Modell, Settings-Injektion) | | L4 | **launcher** | Katalog/Vorlagen + «neue Automation» (Template/AI) + Template-Instanziierung | | L4 | **monitoring** | Läufe, Tasks, Logs (Ops-Sicht) | > **NICHT Teil der Komponente (geteilt, Code-verifiziert) — Komponente *konsumiert* abwärts:** > - **L1 Toolbox** = `workflows/methods/` (Actions) + `workflows/processing/` (ActionExecutor, methodDiscovery, modes) — von Chats/Agents genutzt. > - **Shared Contracts** = `portTypes`/`PORT_TYPE_CATALOG` + Node-Katalog/`STATIC_NODE_TYPES` — von methods/processing/chats/i18n genutzt; bleibt im Shared-Layer (neu `workflows/workflowContracts/`). Die Komponente besitzt sie **nicht**. ### Platform (`platform-core/modules/workflowAutomation/`) ``` modules/workflowAutomation/ # NEU — die Automation-Komponente ├─ mainWorkflowAutomation.py # System-Registrierung (instantiable=False) + Lifespan-Hook (Scheduler-Boot; Poller-Stop) ├─ datamodelWorkflowAutomation.py # AutoWorkflow / AutoVersion / AutoRun / AutoTask (+ Legacy-Aliase) ├─ routes/ # mandatsweite API /api/workflow-automation/{workflows,versions,runs,tasks,nodes,solutions,...} ├─ editor/ # L2 — Graph-Editor-Backend (nodeRegistry, nodeAdapter, adapterValidator) — konsumiert Shared Contracts ├─ engine/ # L2 — Graph-Ausführung (heute workflows/automation2/: executionEngine, executors, graphUtils, runEnvelope, scheduleCron) ├─ scheduler/ # L2 — Scheduler + emailPoller (heute workflows/scheduler/ + graphicalEditor/emailPoller.py) ├─ solutions/ # L3 — solutionService, datamodelSolution (Settings-Injektion, run-as-Principal) └─ launcher/ # L4 — Katalog/Templates + AI-Erstellung + Template-Instanziierung modules/workflows/ # BLEIBT — geteilt (NICHT Teil der Komponente) ├─ methods/ # L1 — Actions (methodAi/Trustee/Sharepoint/Outlook/File/Context/Clickup/Jira/Redmine) ├─ processing/ # L1 — ActionExecutor, methodDiscovery, modes/adaptive, workflowProcessor ├─ workflowContracts/ (NEU) # Shared Contracts: portTypes (PORT_TYPE_CATALOG) + Node-Katalog (STATIC_NODE_TYPES) │ # — heute in graphicalEditor/, hierher delaminieren (genutzt von methods/processing/chats/i18n) └─ workflowManager.py # Chat-/Agent-seitige Workflow-Verarbeitung ``` > **Abgrenzung Komponente ↔ Contracts ↔ Execution (Code-verifiziert, korrigiert).** Drei Schichten, strikt abwärts: **Komponente → Shared Contracts → Execution-Layer.** > - **In die Komponente:** `workflows/automation2/` (Graph-Engine) → `engine/`; `workflows/scheduler/` → `scheduler/`; `graphicalEditor/{nodeRegistry, adapterValidator, emailPoller, Editor-Backend}` → `editor/`/`scheduler/`. > - **Bleibt geteilt unter `modules/workflows/`:** `methods/` (Actions), `processing/` (`ActionExecutor`, `methodDiscovery`, `modes/`, `adaptive/`, `workflowProcessor`) — genutzt von Chats/Agents (`serviceAgent/{actionToolAdapter,mainServiceAgent}`, `serviceAi/*`, `serviceGeneration/*`) **ohne** Automation. > - **Shared Contracts (neu zu delaminieren):** `portTypes`/`PORT_TYPE_CATALOG` und der Node-Katalog/`STATIC_NODE_TYPES` liegen heute unter `graphicalEditor/`, werden aber von `methods/methodBase.py`, `processing/shared/parameterValidation.py`, `serviceAgent/actionToolAdapter.py` und `shared/i18nRegistry.py` genutzt → **bleiben geteilt** (`workflows/workflowContracts/`); die Komponente konsumiert sie. > - **Korrektur eines früheren Fehlers:** «`automation2` wird von Chats nicht importiert» war **falsch**. `methods/methodFile/actions/create.py` + `methods/methodContext/actions/setContext.py` importieren `automation2.executors` (`_coerce_document_data_to_bytes`, `PauseForHumanTaskError`) top-level — geladen via `methodDiscovery` im Chat-/Agent-Pfad. **Diese zwei Helfer vor dem Engine-Umzug in den Shared-Layer extrahieren** (Delaminierung), sonst importiert Shared aufwärts in die Komponente. > > **Guard-Test:** sicherstellen, dass `modules.workflows.{methods,processing}` + `modules.serviceCenter` importierbar sind, **ohne** `modules.workflowAutomation.*` zu laden. Migration nur der Automation-Teile (Re-Export/Shims; ~20 Importer von `workflows.automation2`/`scheduler`). **Kein funktionaler Umbau der Engine** (Ausnahme: per-Node-Billing, AC5). ### UI (`ui-nyla/src/`) ``` pages/workflowAutomation/ ├─ WorkflowAutomationHubPage.tsx # Container: Top-Level-Gruppe «Workflow-Automation», Tabs, Mandanten-/Instanz-Scope-Selector └─ tabs/ ├─ LauncherTab.tsx # L4 — Katalog/Neu (Vorlage oder AI) ├─ SolutionsTab.tsx # L3 — konfigurierte Solutions (Admin/Power-User-Sicht) ├─ EditorTab.tsx # L2 — Graph-Editor (Keep-Alive) ├─ WorkflowsTab.tsx # alle Workflows (cross-mandate/-feature) ├─ RunsTab.tsx # L4 — Läufe/Monitoring └─ TasksTab.tsx # Human-Tasks components/workflowAutomation/ ├─ FlowEditor/ # heute components/GraphicalEditor/ ├─ Launcher/ ├─ Solutions/ └─ Monitoring/ api/workflowAutomationApi.ts # /api/workflow-automation/... (ersetzt workflowApi {instanceId}-Pfade) ``` - **Eine** Top-Level-Nav-Gruppe «Workflow-Automation» (nicht pro Mandant/Instanz); Tabs via `?tab=` deeplinkbar (Muster `AutomationsDashboardPage`). - **Abgrenzung zur kundenseitigen «Lösungen»-Surface:** Der `SolutionsTab` hier ist die **Admin/Power-User**-Sicht über alle Solutions; die kundenseitige `SolutionsView` (L4) bleibt **pro Host-Feature eingebettet** (Trustee usw.) und zeigt nur die für diesen Kontext konfigurierten Solutions. Beide nutzen `api/workflowAutomationApi.ts`. ## Verhältnis zur Solution-Schicht (CustomerCases) - **WorkflowAutomation-Gruppe = Substrat-UI** (Power-User/Admin): roher Editor, Templates, alle Workflows/Läufe/Tasks über Mandanten/Features. - **«Lösungen»-Surface = kundenseitig**, pro Host-Feature eingebettet (Trustee usw.) — konsumiert dasselbe Substrat, zeigt aber nur konfigurierbare Solutions. Bleibt wie in den CustomerCases-Plänen. - Beide hängen am gleichen Modell: **Mandant + Run-as-Principal**, nicht an einer GE-Instanz. Dieser Plan ist die **Voraussetzung** für A0.1/A0.2. ## Betroffene Module - **Gateway (platform-core):** - `app.py` — Scheduler/Email-Poller-Boot in **System-Lifespan** (statt Feature-`onStart`). - `modules/workflows/scheduler/mainScheduler.py` — Boot-Aufruf; `JOB_ID_PREFIX`/`_CALLBACK_NAME` (`"graphicalEditor.*"`) sind in Job-IDs persistiert → Literale behalten oder Job-IDs migrieren (in-memory APScheduler, Re-Sync beim Boot → risikoarm); `if not instanceId`-Guard entfernen (s. Phase 1); **Run-as-Principal** statt globalem `event`-Sysadmin (A0.1). - **Delaminierung zuerst (Phase 0.5):** `PauseForHumanTaskError` + `_coerce_document_data_to_bytes` aus `automation2/executors/` in den Shared-Layer; `portTypes`/Node-Katalog (`STATIC_NODE_TYPES`) aus `graphicalEditor/` nach `workflows/workflowContracts/`. Erst dann ist der Engine-Umzug aufwärts-importfrei. - `modules/workflows/{automation2,scheduler}/` → in die Komponente: `automation2` → `workflowAutomation/engine/`, `scheduler` → `workflowAutomation/scheduler/` (Re-Export-Shims). **`automation2` = die Graph-Engine**, kein funktionaler Umbau (Ausnahme: per-Node-Billing-Kontext, AC5). - **`modules/workflows/{methods,processing}/` + `workflowContracts/` bleiben** geteilt — **nicht** verschieben. - `modules/features/graphicalEditor/` → konzeptionell nach `modules/workflowAutomation/editor/` führen; `mainGraphicalEditor.py`: `getFeatureDefinition`/`TEMPLATE_ROLES`/`onStart` entfernen bzw. zu System-Registrierung umbauen (`instantiable=False`, Vorbild `mainSystem.py`). - `modules/features/graphicalEditor/routeFeatureGraphicalEditor.py` — neue **mandatsweite** Routen `/api/workflow-automation/...`; die **39** `_validateInstanceAccess`-Sites bewusst auf einen neuen `_validateWorkflowAccess(workflow, context, action)` (read vs. write) migrieren — Read-Scoping vom Dashboard, Write/Execute eigens entworfen, `isPlatformAdmin`-Bypass. `_validateTargetInstance` zusätzlich um **Mandatsgrenze** ergänzen (AC8, heute nur `FeatureAccess`). - `modules/routes/routeSystem.py` — GE-Hardcode in `_getFeatureUiObjects` entfernen; neuen statischen Nav-Block `workflowAutomation` ausliefern. - `modules/system/mainSystem.py` — `NAVIGATION_SECTIONS`: Block `id="workflowAutomation"` (order ~25) + `ui.system.workflowAutomation.*`-Objekte; Store-Eintrag `resource.store.graphicalEditor` entfernen. - `modules/interfaces/interfaceFeatures.py` — `_copyTemplateWorkflows` läuft heute bei **FeatureInstance-Erstellung** und stempelt `{{featureInstanceId}}`/`targetFeatureInstanceId` (auch für Trustee/Redmine). Ohne GE-Instanz **entfällt der Trigger** → Template-Instanziierung explizit in den `launcher/`-Flow verlagern (inkl. Demo-Bootstrap); Trustee/Redmine-Platzhalter-Vertrag beachten. - `modules/interfaces/interfaceDbApp.py` — Mandate-Delete-Cascade für WorkflowAutomation-Daten mandatsweit (statt per GE-Instanz; Vorbild `_cascadeDeleteAutoWorkflow`). - `modules/interfaces/interfaceRbac.py` — `TABLE_NAMESPACE` (8 Einträge: `AutoWorkflow/AutoVersion/AutoRun/AutoStepLog/AutoTask` + Legacy `Automation2*`): **Option B** — Namespace `feature.graphicalEditor` **beibehalten** + System-DATA-Objekte ergänzen. (Ein Flip auf `system.workflowAutomation` verwaist bestehende `AccessRule`/Rollen-Zeilen; nur mit zwingend gekoppeltem Migrations-Skript im selben Deploy.) - `modules/interfaces/interfaceBootstrap.py` — Store-Grant + ggf. neue System-Rollen `workflowAutomation-*`. - `modules/features/graphicalEditor/mainGraphicalEditor.py` `getGraphicalEditorServices` — `featureCode`/`featureInstanceId` im Hub: Billing **per-Node** auf berührte Instanz (A0.2, Executor-Change), Mandate = `mandateId`. - `serviceCenter/.../toolboxRegistry.py` — `ToolboxDefinition(featureCode="graphicalEditor")` → `workflowAutomation`. - **Weitere GE-Importer (oft übersehen):** `shared/i18nRegistry.py` (zieht `STATIC_NODE_TYPES`/`PORT_TYPE_CATALOG` → nach Delaminierung auf `workflowContracts` umbiegen), `routeSystem.py` (`UI_OBJECTS` + Dashboard-`_ensureTableExists`), `routeAdminFeatures.py`, `interfaceDbManagement.py`, `interfaceFeatureGraphicalEditor.getAllWorkflowsForScheduling` (Lesepfad von Scheduler **und** Dashboard — stabil halten/shimmen). - **Frontend (ui-nyla):** - `src/components/Navigation/MandateNavigation.tsx` — `block.id === 'workflowAutomation'` als eigene Top-Level-Gruppe rendern (analog `admin`). - `src/App.tsx` — `/workflow-automation/*`-Routen (Hub + `?tab=` Deeplinks); Redirects der alten `/mandates/.../graphicalEditor/.../*`. - `src/pages/AutomationsDashboardPage.tsx` → in **`WorkflowAutomationHubPage`** mit Tabs überführen (Workflows, Läufe, Editor, Vorlagen, Tasks). - `src/pages/FeatureView.tsx` — `graphicalEditor` aus `VIEW_COMPONENTS` entfernen. - `src/pages/views/graphicalEditor/*` — von `useInstanceId()`/`useCurrentInstance()` auf expliziten Mandanten-/Instanz-Picker (Scope-Selector) umstellen. - `src/config/keepAliveRoutes.tsx` — Editor-Keep-Alive-Pfad auf `/workflow-automation/editor` umziehen. - `src/config/pageRegistry.tsx` — Icons für `page.system.workflowAutomation.*`. - `src/api/workflowApi.ts` — URLs `/api/workflows/{instanceId}/…` → `/api/workflow-automation/…`. - Cross-Links: `TrusteeAnalyseView`/`TrusteeAbschlussView` (`/automations?tab=…`), `Store.tsx` (GE-Karte entfernen). - **DB-Migration:** minimal — `runAsPrincipal` = additives `ADD COLUMN`; `featureInstanceId` ist **bereits DB-nullable** (kein DDL, keine DB-FK; nur Pydantic `Optional` + Code); Daten-/Grant-Backfill via Skript. **Kein** Namespace-Flip (Option B). - **Andere:** RBAC (System-Objekte/-Rollen, FeatureAccess-Grants migrieren, GE-Feature-Rollen entfallen), Billing (per-Node-Attribution), Demo-Configs (`pwgDemo2026`/`investorDemo2026`), Tests, `.cursor/rules/*` (s. Abschluss). ## Entscheidungen | Datum | Entscheidung | Begründung | |-------|-------------|------------| | 2026-06-05 | System-Komponente heisst **`WorkflowAutomation`** (Token `WorkflowAutomation`/`workflowAutomation`); UI-Label «Workflow-Automation» | semantisch eindeutig + greppbar; generisches «Automation» bleibt für Bestand | | 2026-06-05 | `graphicalEditor` = **ein Modul** (editor) innerhalb von WorkflowAutomation | graphEditor ist nur das Authoring-Element | | 2026-06-05 | **Automation ≠ Workflow-Execution.** `methods`/`processing` bleiben geteilter Layer unter `modules/workflows/`; nur `automation2` (engine) + `scheduler` + `graphicalEditor` wandern in die Komponente | Chats/Agents nutzen den Execution-Layer **ohne** Automation; Abwärts-Abhängigkeit Komponente→Execution (Code-belegt) | | 2026-06-05 | **Shared Contracts** (`portTypes`/`PORT_TYPE_CATALOG`, Node-Katalog) bleiben geteilt (`workflows/workflowContracts/`); Komponente konsumiert | Code-Beleg: methods/processing/chats/i18n nutzen sie — Verschieben = Aufwärts-Import | | 2026-06-05 | **Delaminierung** zweier Helfer (`PauseForHumanTaskError`, `_coerce_document_data_to_bytes`) aus `automation2.executors` **vor** Engine-Umzug | sonst importiert Shared `methods/` aufwärts in die Komponente | | 2026-06-05 | RBAC-Namespace **Option B** (beibehalten + System-DATA-Objekte), **kein** Flip | verhindert verwaiste `AccessRule`/Rollen-Zeilen | | 2026-06-05 | Eigener **Write-RBAC-Helper** `_validateWorkflowAccess` (read/write) für die 39 Sites; GE-Feature-Rollen entfallen, Modell = Mandats-Mitgliedschaft/-Admin + `isPlatformAdmin` | Dashboard-Muster deckt nur Lesen; `instantiable=False` ⇒ keine Feature-Instanz-Rollen mehr | | 2026-06-05 | **Modulares System** (editor/engine/scheduler/solutions/launcher/monitoring), gemappt auf L2–L4; L1 (Actions/Execution) + Contracts bleiben geteilt | wartbar/nachvollziehbar; bündelt heute verstreute Automation-Teile + L3/L4 | | 2026-06-05 | Code nach **`modules/workflowAutomation/`** (Platform) bzw. `pages/workflowAutomation/` + `components/workflowAutomation/` (UI); schrittweise via Re-Export-Shims | sauberer Schnitt; Imports brechen nicht | | 2026-06-05 | `graphicalEditor` wird **System-Komponente** (Feature-Mantel entfällt) | kein DATA, eigene DB für alle Features, globaler Scheduler-Boot → ist Infrastruktur | | 2026-06-05 | Eigene Top-Level-Nav-Gruppe **«Workflow-Automation»** mit Tab-Seiten | strukturiert an einem Ort, cross-mandate/-feature; nicht als Feature-Seite | | 2026-06-05 | Scheduler-/Poller-Boot in **System-Lifespan** (`app.py`) statt Feature-`onStart` | Boot von der Feature-Plug-in-Mechanik lösen | | 2026-06-05 | API mandatsweit `/api/workflow-automation/…`; RBAC = Mandats-Mitgliedschaft + System-Rollen + `isPlatformAdmin` | `{instanceId}` als RBAC-Anker abbauen; bestehende Dashboard-Muster wiederverwenden | | 2026-06-05 | `AutoWorkflow.featureInstanceId` verliert RBAC-Bedeutung; `mandateId`+`runAsPrincipal` = Owner; `targetFeatureInstanceId`/per-Node-Ref bleiben | A0.1/A0.2; Daten-Scope bleibt funktional | | 2026-06-05 | Migration **phasenweise, parallel** (kein Big-Bang); Bestand grandfathern | Risiko begrenzen, jederzeit lauffähig | | 2026-06-05 | DB-Name `poweron_graphicaleditor` bleibt | YAGNI; Rename = unnötiges Risiko | ## Umsetzungs-Checkliste **Phase 0 — Scheduler-Boot entkoppeln (risikoarm, zuerst):** - [ ] Scheduler-Start in System-Lifespan (`app.py`, **nach** `setSchedulerMainLoop`; `eventUser` ist dort schon vorhanden); Poller bleibt **on-demand** (`ensureRunning`), nur `onStop`-Stop verlagern; GE-`onStart`/`onStop` entfernen - [ ] Sicherstellen, dass das Komponenten-Modul weiter importiert wird (Routen-Registrierung) auch ohne Feature-Mantel - [ ] Smoke: **bestehende (grandfathered)** geplante Runs feuern weiterhin, auch ohne GE-Feature-Instanz im Mandanten **Phase 0.5 — Delaminierung (vor jedem Code-Umzug):** - [ ] `PauseForHumanTaskError` + `_coerce_document_data_to_bytes` aus `automation2/executors/` in den Shared-Layer ziehen (Re-Export-Shim am alten Ort) - [ ] `portTypes`/`PORT_TYPE_CATALOG` + Node-Katalog (`STATIC_NODE_TYPES`) → `workflows/workflowContracts/`; `i18nRegistry`, `methodBase`, `parameterValidation`, `actionToolAdapter` darauf umbiegen - [ ] Guard-Test: `modules.workflows.{methods,processing}` + `serviceCenter` importieren **ohne** `modules.workflowAutomation.*` **Phase 1 — Mandatsweite API + System-RBAC (parallel zur Bestands-API):** - [ ] `AutoWorkflow.runAsPrincipal` ergänzen (additives `ADD COLUMN`, nullable) - [ ] System-RBAC-Objekte `ui.system.workflowAutomation.*` + RESOURCE für `/api/workflow-automation/…`; DATA-Objekte unter **bestehendem** Namespace (Option B) - [ ] Neue Routen `/api/workflow-automation/{workflows,versions,runs,tasks,nodes,…}` mandatsweit; **Write-RBAC-Helper** `_validateWorkflowAccess` (read = Dashboard-Scoping, write/execute eigens), `isPlatformAdmin`-Bypass - [ ] Scheduler **von `featureInstanceId` entkoppeln**: `if not instanceId`-Guard (`mainScheduler.py`) entfernen, Interface/Services nicht mehr instanz-gekeyt auflösen, Ausführung über `targetFeatureInstanceId`/Mandant scopen - [ ] Per-Node-Billing: berührte `FeatureInstanceRef` in den `recordUsage`-Kontext threaden (Executor-Change, AC5), Mandate = `mandateId` - [ ] Run-as-Principal in Engine/Scheduler (statt globalem `event`-Sysadmin) + Read-RBAC analog Write-Pfad (A0.1) - [ ] Runtime-Mandatsvalidierung beim `FeatureInstanceRef`-Auflösen (A0.2) **Phase 2 — UI «Workflow-Automation»-Gruppe:** - [ ] Nav-Block `workflowAutomation` in `mainSystem.py` + Handling in `MandateNavigation.tsx` (Top-Level) - [ ] `/workflow-automation/*`-Routen + Hub-Seite mit Tabs (Editor · Vorlagen · Workflows · Läufe · Tasks), `?tab=`-Deeplinks - [ ] Mandanten-/Instanz-Scope-Selector in der Seite (kein per-Instanz-Nav) - [ ] Editor-Komponenten von `useInstanceId()` lösen; Keep-Alive-Pfad umziehen - [ ] Redirects alte → neue Pfade; Cross-Links (Trustee, Store) anpassen **Phase 3 — DB & Datenmigration:** - [ ] `AutoWorkflow.featureInstanceId` → `Optional` (Pydantic; **kein DDL** — Spalte bereits nullable, keine DB-FK); Routen/Queries auf leeren Wert tolerant machen; `validateFkTargets` tolerant - [ ] `Solution`-Modell vorbereiten (A0.1 — Detail in CustomerCases-features-plan) - [ ] Migrations-Skript: `FeatureAccess`-Grants → Mandats-Mitgliedschaft/-Rollen (GE-Feature-Rollen entfallen — **Sichtbarkeitsänderung dokumentieren**); `featureInstanceId`-RBAC-Bezug auflösen (Bestand grandfathern), `targetFeatureInstanceId` erhalten - [ ] Mandate-Delete-Cascade mandatsweit (Vorbild `_cascadeDeleteAutoWorkflow`) **Phase 4 — Feature-Mantel entfernen:** - [ ] `getFeatureDefinition`/`registerFeature`/`TEMPLATE_ROLES`/`UI_OBJECTS`(feature)/Store-Eintrag entfernen; GE-Hardcode in `routeSystem.py` raus - [ ] `FeatureView.tsx`-Eintrag + Store-Karte entfernen; Demo-Configs umstellen - [ ] Alte `/api/workflows/{instanceId}/…`-Routen deprecaten/entfernen - [ ] Tests grün (inkl. `tests/unit/graphicalEditor/*`, Demo-Bootstrap, Adapter-Drift) **Abschluss:** - [ ] `b-reference` aktualisieren; `_CHANGELOG.md` ## Akzeptanzkriterien | # | Kriterium (Given-When-Then) | Prio | |---|---|---| | 1 | Given ein Mandant ohne GE-FeatureInstanz, When ein **bestehender (grandfathered)** geplanter Workflow fällig ist, Then feuert der Scheduler (Boot via System-Lifespan). *(Instanz-freie Workflows: erst nach Phase-1-Scheduler-Entkopplung.)* | must | | 2 | Given ein User mit Mandats-Mitgliedschaft, When er «Workflow-Automation» öffnet, Then sieht er eine Top-Level-Gruppe mit Tabs (Editor/Vorlagen/Workflows/Läufe/Tasks) — **nicht** pro Mandant/Instanz dupliziert | must | | 3 | Given mehrere Mandanten/Features, When er Workflows/Läufe ansieht, Then sind sie cross-mandate/-feature gelistet, RBAC-gefiltert (Mandats-Admin/`isPlatformAdmin`) | must | | 4 | Given Bestands-Workflows/Runs/Tasks, When migriert wurde, Then bleiben sie aufrufbar/ausführbar; `targetFeatureInstanceId` unverändert wirksam | must | | 5 | Given ein Run über mehrere Trustee-Instanzen, When er läuft, Then bucht `recordUsage` je Node die berührte Instanz (nicht fix `graphicalEditor`) | must | | 6 | Given kein `graphicalEditor`-Feature mehr, When der Store geöffnet wird, Then erscheint keine GE-Karte; bestehende Mandanten verlieren keine Workflows | must | | 7 | Given ein geplanter Run, When er ausgeführt wird, Then unter definierter Automations-Identität (nicht globaler Sysadmin); Reads nur im RBAC-Umfang des Principals | must | | 8 | Given die neue API, When ein Node eine fremd-mandatige Instanz-UUID trägt, Then Laufzeit-Fehler (Mandatsgrenze) | should | ## Testplan | ID | AC | Art | Automatisiert | Repo-Pfad | Status | |----|----|-----|--------------|-----------|--------| | T1 | 1 | integration | ja | platform-core/tests/.../scheduler | pending | | T2 | 3 | api | ja | platform-core/tests/.../workflowAutomation | pending | | T3 | 4 | migration | ja | platform-core/tests/.../migration | pending | | T4 | 5 | unit | ja | platform-core/tests/.../billing | pending | | T5 | 2 | e2e/manual | nein | ui-nyla | pending | ## Offene Fragen 1. **RBAC-Granularität (grösstenteils entschieden):** Modell = Mandats-Mitgliedschaft + Mandats-Admin + `isPlatformAdmin`; GE-Feature-Rollen (`graphicalEditor-*`) entfallen. Offen nur: brauchen Power-User ohne Admin eine eigene System-Rolle `workflowAutomation-user`, oder genügt Mandats-`user`? (Feature-Instanz-Rollen sind keine Option mehr — `instantiable=False` + `rbac-role-separation.mdc`.) 2. **API-Pfad:** `/api/workflow-automation/…` neu vs. bestehendes `/api/system/workflow-runs` ausbauen — konsolidieren? 3. **Run-as-Principal-Verwaltung:** Ersteller-Identität vs. dedizierter Service-Principal (siehe CustomerCases offene Frage #1). 4. **Alt-URLs:** wie lange Redirects der `{instanceId}`-Pfade halten (Bookmarks/Deeplinks)? 5. **Datei-/Doku-Rename:** dieses Plan-Dokument ggf. auf `…-workflowautomation-system-component.md` umbenennen (aktuell `…-automation-system-component.md`) — Konsistenz vs. Referenz-Churn. ## Links - Architektur/Entscheid: `c-work/0-ideas/2026-06-CustomerCases-step1-architecture.md` (A0.4), `…-step3-features-plan.md` (A0) - Prior Art: `c-work/4-done/2026-04-automation-unification.md`, `2026-04-automation-central-admin.md`, `2026-04-generic-graph-editor.md` - Code (Backend): `modules/features/graphicalEditor/`, `modules/workflows/{automation2,scheduler}/`, `modules/routes/{routeWorkflowDashboard,routeAutomationWorkspace,routeSystem}.py`, `modules/system/mainSystem.py`, `app.py` - Code (Frontend): `ui-nyla/src/pages/AutomationsDashboardPage.tsx`, `components/Navigation/MandateNavigation.tsx`, `pages/FeatureView.tsx`, `App.tsx` - RBAC: `b-reference/platform/rbac.md`, `.cursor/rules/rbac-role-separation.mdc`, `.cursor/rules/feature-instance-scoping.mdc` ## Abschluss - [ ] `b-reference/`: neue Kanon-Seite «WorkflowAutomation (System-Komponente)»; `graphicalEditor`-Feature-Seite umschreiben - [ ] `.cursor/rules/rbac-role-separation.mdc` + `feature-instance-scoping.mdc` aktualisieren (WorkflowAutomation als dokumentierte Ausnahme; stale Globs `gateway/`/`frontend_nyla/` → `platform-core/`/`ui-nyla/` korrigieren) - [ ] `TOPICS.md` aktualisieren (WorkflowAutomation = System-Komponente) - [ ] **Cross-Import Assessment** (nach Umsetzung): Import-Analyse erneut durchfuehren und pruefen: - [ ] `workflows -> features.graphicalEditor` (~40 Imports): Alle via Komponenten-Umzug eliminiert? - [ ] `serviceCenter -> features.graphicalEditor` (5x workflowTools): entfaellt? - [ ] `interfaces/interfaceDbManagement.py:936` -> `workflowArtifactVisibility`: mit Engine-Umzug erledigt? - [ ] Re-Export-Shims (`datamodelFeatureGraphicalEditor.py`, `stateTools.py`): entfernbar? - [ ] Verbleibende interne Zyklen in `workflows/` (30 lazy Imports): reduziert nach Restructuring? - [ ] Finale Folder-Summary und Import-Zahlen neu generieren - [ ] `local/notes/refernce-analysis/platform-core-import-analyse.md` finalisieren - [ ] Dieses Dokument → `c-work/2-build/` bei Annahme, dann `4-done/`