wiki/c-work/1-plan/2026-06-automation-system-component.md

284 lines
33 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<!-- status: plan -->
<!-- started: 2026-06-05 -->
<!-- component: gateway | ui-nyla | platform -->
# 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 L2L4-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 L2L4; 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)
- [ ] Dieses Dokument → `c-work/2-build/` bei Annahme, dann `4-done/`