372 lines
48 KiB
Markdown
372 lines
48 KiB
Markdown
<!-- status: done -->
|
||
<!-- started: 2026-06-05 -->
|
||
<!-- completed: 2026-06-08 -->
|
||
<!-- component: gateway | ui-nyla | platform -->
|
||
|
||
# WorkflowAutomation als System-Komponente — `graphicalEditor` raus aus dem Feature-Modell
|
||
|
||
> **Migration Status: COMPLETE (2026-06-08).** Alle `graphicalEditor`-Code-Referenzen eliminiert. Code aus `features/graphicalEditor/`, `workflows/automation2/`, `workflows/scheduler/` verschoben nach `modules/workflowAutomation/{editor,engine,scheduler}/`. Originale gelöscht, `__init__.py`-Shims als Safety-Net. Test-Imports, DEPRECATED-Kommentare, Store/RBAC-Namespace, Demo-Configs, Frontend-Kommentare/Logs bereinigt. `graphicalEditorRunFileLogger.py` → `runFileLogger.py`. `datamodelFeatureGraphicalEditor.py` gelöscht. Frontend-Monolith gesplittet (D1-D3). RBAC-Migration als idempotente Boot-Routine. Verbleibend: AC5 per-Node Billing + AC7 Run-as-Principal (Scope CustomerCases), automatisierte Tests T1-T5 (pending), Smoke-Test Scheduler (manuell bei Deploy).
|
||
|
||
> **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.
|
||
|
||
## Code-Stand nach Import-Refactoring (2026-06-05/06)
|
||
|
||
Das Backend wurde zwischenzeitlich auf eine **Layer-Hierarchie L0–L7** refactored (`shared`=L0, `datamodels`=L1, `connectors`=L2, `dbHelpers`=L3, `interfaces`/`system`/`security`/`auth`=L4, `serviceCenter`/`workflows`/`features`=L5, `routes`=L6, `app`=L7; Quelle: `local/notes/refernce-analysis/platform-core-import-analyse.md`). Das ändert mehrere Plan-Annahmen — vieles ist **schon erledigt**; das GE↔Engine-Coupling wurde dort **explizit auf diesen Plan vertagt** («Deferred: WorkflowAutomation»).
|
||
|
||
**Schon erledigt (Plan-Schritte entfallen/reduziert):**
|
||
- **Contracts + Models liegen in L1:** `PORT_TYPE_CATALOG`/`PRIMITIVE_TYPES` → `datamodels/datamodelPortTypes.py`; `AutoWorkflow/AutoVersion/AutoRun/AutoStepLog/AutoTask` → `datamodels/datamodelWorkflowAutomation.py` (kanonisch, Re-Export-Shim `features/graphicalEditor/datamodelFeatureGraphicalEditor.py`, 7 Caller). → Das geplante «Shared Contracts»-Modul **ist faktisch `datamodels` (L1)**; **kein** neues `workflowContracts/` nötig.
|
||
- **Feature-Lifecycle-Hooks existieren:** `mainGraphicalEditor.onInstanceCreate()` (Template-Copy, ex `interfaceFeatures._copyTemplateWorkflows`), `onMandateDelete()` (Cascade, ex `interfaceDbApp`), `onBootstrap()` (Seeding, ex `interfaceBootstrap`) — dynamisch via `shared/featureDiscovery.loadFeatureMainModules()`. → Plan-Touchpoints in `interfaceFeatures/interfaceDbApp/interfaceBootstrap` sind jetzt **Feature-Hooks**.
|
||
- **Weitere Verschiebungen:** `NAVIGATION_SECTIONS` → `datamodels/datamodelNavigation.py`; `EventManager` → `shared/eventManager.py`; `parseInlineRuns` → `shared/documentUtils.py`; `serviceHub` → `serviceCenter/serviceHub.py`; Service-Exceptions → `datamodels/serviceExceptions.py`; `WorkflowStoppedException`/`checkWorkflowStopped` → `shared/workflowState.py`.
|
||
|
||
**Explizit auf diesen Plan vertagt (Deferred-Liste):**
|
||
- `features.graphicalEditor → workflows` (21) + `workflows → features.graphicalEditor` (40) → Ziel **0** (kollabiert, sobald Editor + Engine **ein** Modul sind).
|
||
- `serviceCenter → features.graphicalEditor` (5×, `serviceAgent/workflowTools.py`).
|
||
- `interfaces/interfaceDbManagement.py:936` (lazy → `workflowArtifactVisibility`).
|
||
- Re-Export-Shims entfernen: `datamodelFeatureGraphicalEditor.py` (7 Caller), `workflows/processing/shared/stateTools.py` (7 Caller).
|
||
- **Boundary-Leck besteht weiter (Delaminierung offen):** `PauseForHumanTaskError` (`automation2/executors/inputExecutor.py`) und `coerceDocumentDataToBytes` (`automation2/executors/actionNodeExecutor.py`) werden von `methods/methodContext/actions/setContext.py` bzw. `methods/methodFile/actions/create.py` top-level importiert → vor Engine-Umzug extrahieren (`PauseForHumanTaskError` → `datamodels/serviceExceptions.py`, `coerceDocumentDataToBytes` → `shared/documentUtils.py`).
|
||
|
||
## 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 schon in L1, Node-Katalog noch beim Editor.** `PORT_TYPE_CATALOG` liegt seit dem Refactoring in `datamodels/datamodelPortTypes.py` (erledigt). Der Node-Katalog (`STATIC_NODE_TYPES`) ist noch unter `features/graphicalEditor/nodeDefinitions/` und wird cross-layer genutzt (`system/i18nBootSync.py`, `serviceAgent/workflowTools.py`) — er zieht mit dem Editor in die Komponente. **Offenes Boundary-Leck:** zwei Helfer (`PauseForHumanTaskError`, `coerceDocumentDataToBytes`) liegen in `automation2.executors`, werden aber von `methods/` importiert → **vor** dem Engine-Umzug 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 (Execution)** = `workflows/methods/` (generische Actions) + `workflows/processing/` (ActionExecutor, methodDiscovery, modes) — von Chats/Agents genutzt. (Feature-eigene Actions liegen seit Refactoring beim Feature, z. B. `features/trustee/workflows/`.)
|
||
> - **Contracts/Models = `datamodels` (L1, bereits erledigt):** `datamodelPortTypes.py` (`PORT_TYPE_CATALOG`), `datamodelWorkflowAutomation.py` (`AutoWorkflow…`). Die Komponente konsumiert L1, besitzt sie **nicht**. (Der Node-Katalog `STATIC_NODE_TYPES` liegt noch beim Editor und zieht mit ihm um.)
|
||
|
||
### Platform (`platform-core/modules/workflowAutomation/`)
|
||
|
||
```
|
||
modules/workflowAutomation/ # NEU — die Automation-Komponente
|
||
├─ mainWorkflowAutomation.py # System-Registrierung (instantiable=False) + Lifespan-Hook (Scheduler-Boot; Poller-Stop) + Lifecycle-Hooks (onMandateDelete/onBootstrap)
|
||
│ # Models kanonisch in datamodels/datamodelWorkflowAutomation.py (L1) — Komponente importiert sie
|
||
├─ 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 — geteilter Execution-Layer (NICHT Teil der Komponente)
|
||
├─ methods/ # L1-Toolbox — generische Actions (methodAi/Sharepoint/Outlook/File/Context/Clickup/Jira/Redmine)
|
||
├─ processing/ # ActionExecutor, methodDiscovery, modes/adaptive, workflowProcessor
|
||
└─ workflowManager.py # Chat-/Agent-seitige Workflow-Verarbeitung
|
||
|
||
modules/datamodels/ # BLEIBT — Contracts/Models (L1, bereits dorthin refactored)
|
||
├─ datamodelPortTypes.py # PORT_TYPE_CATALOG / PRIMITIVE_TYPES
|
||
└─ datamodelWorkflowAutomation.py# AutoWorkflow / AutoVersion / AutoRun / AutoStepLog / AutoTask (kanonisch)
|
||
```
|
||
|
||
> **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.
|
||
> - **Contracts/Models:** `PORT_TYPE_CATALOG` + `AutoWorkflow…` liegen **bereits** in `datamodels/` (L1, durchs Refactoring); Komponente konsumiert sie. Der Node-Katalog (`STATIC_NODE_TYPES`) liegt noch beim Editor und zieht mit ihm um (cross-Caller: `system/i18nBootSync.py`, `serviceAgent/workflowTools.py` beachten).
|
||
> - **Korrektur eines früheren Fehlers (weiterhin gültig):** «`automation2` wird von Chats nicht importiert» war **falsch** und ist **noch nicht behoben**. `methods/methodFile/actions/create.py` (→ `coerceDocumentDataToBytes`) + `methods/methodContext/actions/setContext.py` (→ `PauseForHumanTaskError`) importieren `automation2.executors` top-level — geladen via `methodDiscovery` im Chat-/Agent-Pfad. **Diese zwei Helfer vor dem Engine-Umzug extrahieren** (`→ datamodels/serviceExceptions.py` bzw. `shared/documentUtils.py`), 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` → `datamodels/serviceExceptions.py`, `coerceDocumentDataToBytes` → `shared/documentUtils.py` (Re-Export-Shim in `automation2/executors/`). Contracts (`portTypes`, Models) sind bereits in `datamodels` (L1). 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}/` bleiben** geteilt (Execution-Layer), **`datamodels/`** bleibt L1 — **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/datamodels/datamodelNavigation.py` — `NAVIGATION_SECTIONS` (post-Refactoring hier, **nicht** mehr in `mainSystem.py`): Block `id="workflowAutomation"` (order ~25) ergänzen; `ui.system.workflowAutomation.*`-Objekte; Store-Eintrag `resource.store.graphicalEditor` entfernen.
|
||
- `modules/features/graphicalEditor/mainGraphicalEditor.py` **`onInstanceCreate()`** — heute Template-Copy bei FeatureInstance-Erstellung (stempelt `{{featureInstanceId}}`/`targetFeatureInstanceId`, auch Trustee/Redmine). Ohne GE-Instanz **entfällt der Hook-Trigger** → Template-Instanziierung explizit in den `launcher/`-Flow verlagern (inkl. Demo-Bootstrap); Trustee/Redmine-Platzhalter-Vertrag beachten.
|
||
- `modules/features/graphicalEditor/mainGraphicalEditor.py` **`onMandateDelete()`** (Cascade) / **`onBootstrap()`** (Seeding) — sind bereits Feature-Hooks (ex `interfaceDbApp`/`interfaceBootstrap`); ziehen mit der Komponente um, Cascade auf mandatsweit umstellen.
|
||
- `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-*` (Hook-Aufruf bleibt; GE-Wissen ist dynamisch via `loadFeatureMainModules`).
|
||
- `modules/features/graphicalEditor/mainGraphicalEditor.py` `getGraphicalEditorServices` — `featureCode`/`featureInstanceId` im Hub (jetzt `serviceCenter/serviceHub.py`): Billing **per-Node** auf berührte Instanz (A0.2, Executor-Change), Mandate = `mandateId`.
|
||
- `serviceCenter/.../toolboxRegistry.py` + `serviceCenter/services/serviceAgent/workflowTools.py` — `featureCode="graphicalEditor"` → `workflowAutomation`; `workflowTools` ist der verbleibende `serviceCenter → features.graphicalEditor` (5×, deferred) → entfällt mit Umzug.
|
||
- **Weitere GE-Importer (Deferred-Liste):** `system/i18nBootSync.py` (zieht `STATIC_NODE_TYPES` — mit Node-Katalog mitziehen), `routeSystem.py` (`UI_OBJECTS` + Dashboard-`_ensureTableExists`), `routeAdminFeatures.py`, `interfaceDbManagement.py:936` (lazy `workflowArtifactVisibility`), `interfaceFeatureGraphicalEditor.getAllWorkflowsForScheduling` (Lesepfad von Scheduler **und** Dashboard — stabil halten/shimmen); Re-Export-Shims `datamodelFeatureGraphicalEditor.py` (7) + `processing/shared/stateTools.py` (7) entfernen.
|
||
- **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-07 | **Contracts/Models in `datamodels` (L1)** statt neuem `workflowContracts/` — durchs Import-Refactoring bereits erledigt (`datamodelPortTypes`, `datamodelWorkflowAutomation`) | Layer-Hierarchie L0–L7; Komponente konsumiert L1 abwärts |
|
||
| 2026-06-07 | **Delaminierung** zweier Helfer (`PauseForHumanTaskError` → `datamodels/serviceExceptions.py`, `coerceDocumentDataToBytes` → `shared/documentUtils.py`) aus `automation2.executors` **vor** Engine-Umzug | Boundary-Leck besteht weiter: Shared `methods/` importiert sie heute aufwärts |
|
||
| 2026-06-07 | Plan an Import-Refactoring (2026-06-05/06) angeglichen; Deferred-Liste aus `import-analyse.md` als Scope übernommen | refactor hat GE↔Engine-Coupling explizit an diesen Plan delegiert |
|
||
| 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 A — Code-Migration (Copy to new locations + re-export shims):**
|
||
- [x] A2: `modules/workflows/automation2/*.py` → `modules/workflowAutomation/engine/` (executionEngine, graphUtils, runEnvelope, scheduleCron, graphicalEditorRunFileLogger, pickNotPushMigration, featureInstanceRefMigration, workflowArtifactVisibility, clickupTaskUpdateMerge, udmUpstreamShapes) *(2026-06-07)*
|
||
- [x] A2: `modules/workflows/automation2/executors/*.py` → `modules/workflowAutomation/engine/executors/` (actionNodeExecutor, inputExecutor, flowExecutor, triggerExecutor, ioExecutor, dataExecutor) *(2026-06-07)*
|
||
- [x] A3: `modules/workflows/scheduler/mainScheduler.py` → `modules/workflowAutomation/scheduler/mainScheduler.py` *(2026-06-07)*
|
||
- [x] A5: `modules/features/graphicalEditor/{portTypes,adapterValidator,nodeRegistry,nodeAdapter,switchOutput,entryPoints,conditionOperators,upstreamPathsService,_workflowFileSchema}.py` → `modules/workflowAutomation/editor/` *(2026-06-07)*
|
||
- [x] A5: `modules/features/graphicalEditor/nodeDefinitions/*.py` → `modules/workflowAutomation/editor/nodeDefinitions/` *(2026-06-07)*
|
||
- [x] A5: `modules/features/graphicalEditor/emailPoller.py` → `modules/workflowAutomation/scheduler/emailPoller.py` *(2026-06-07)*
|
||
- [x] Re-export shims created at old `__init__.py` locations for backward compatibility
|
||
|
||
**Phase F — Delete old originals (cleanup):**
|
||
- [x] F1: Deleted all individual .py files from `modules/features/graphicalEditor/` (shims: interfaceFeatureGraphicalEditor.py, mainGraphicalEditor.py; originals: portTypes, adapterValidator, nodeRegistry, nodeAdapter, switchOutput, entryPoints, conditionOperators, upstreamPathsService, _workflowFileSchema, emailPoller + all nodeDefinitions/*.py) *(2026-06-07)*
|
||
- [x] F2: Deleted all individual .py files from `modules/workflows/automation2/` and `executors/` (executionEngine, graphUtils, runEnvelope, scheduleCron, graphicalEditorRunFileLogger, pickNotPushMigration, featureInstanceRefMigration, workflowArtifactVisibility, clickupTaskUpdateMerge, udmUpstreamShapes + 6 executor files) *(2026-06-07)*
|
||
- [x] F3: Deleted `modules/workflows/scheduler/mainScheduler.py` *(2026-06-07)*
|
||
- [x] F4: `nodeDefinitions/__init__.py` converted to re-export shim (imports STATIC_NODE_TYPES from new location)
|
||
- [x] F4: `routeWorkflowAutomation.py` imports updated to `modules.workflowAutomation.editor._workflowFileSchema`
|
||
- [x] `automation2/__init__.py`, `automation2/executors/__init__.py`, `scheduler/__init__.py` retained as re-export shims → backward compat for remaining importers
|
||
- [x] `datamodelFeatureGraphicalEditor.py` gelöscht (keine Caller mehr) *(2026-06-08)*
|
||
- [x] Test file imports (`tests/unit/`, `tests/integration/`, `tests/demo/`) updated to canonical `modules.workflowAutomation.*` paths *(2026-06-08)*
|
||
|
||
**Phase D — Frontend Struktur (D1-D3):**
|
||
- [x] D1: `WorkflowAutomationPage.tsx` monolith (1566 lines) split into `pages/workflowAutomation/` with Hub + 5 tab components + types.ts *(2026-06-08)*
|
||
- [x] D2: Editor views re-exported from `pages/workflowAutomation/views/` *(2026-06-08)*
|
||
- [x] D3: FlowEditor components re-export shim at `components/workflowAutomation/FlowEditor/` *(2026-06-08)*
|
||
|
||
**Phase A10 — RBAC Namespace Migration (Boot-integriert):**
|
||
- [x] `_migrateRbacNamespace()` as idempotent routine in `onBootstrap()` — migrates AccessRule objectKeys from `feature.graphicalEditor` to `system.workflowAutomation` on every boot *(2026-06-08)*
|
||
- [x] `TABLE_NAMESPACE` in `interfaceRbac.py` updated from `feature.graphicalEditor` to `system.workflowAutomation` *(2026-06-08)*
|
||
- [x] Standalone script `scripts/script_db_migrate_rbac_workflow_automation.py` retained as manual fallback *(2026-06-07)*
|
||
|
||
**Cleanup — Stale DEPRECATED-Bereinigung:**
|
||
- [x] Stale DEPRECATED-Kommentare entfernt: `i18nBootSync.py` (2x), `workflowTools.py` (5x) — Imports zeigen bereits auf `modules.workflowAutomation.*` *(2026-06-08)*
|
||
- [x] `resource.store.graphicalEditor` → `resource.store.workflowAutomation` in `interfaceBootstrap.py` + `mainSystem.py` *(2026-06-08)*
|
||
- [x] `toolboxRegistry.py`: `featureCode="graphicalEditor"` → `"workflowAutomation"` *(2026-06-08)*
|
||
- [x] `datamodelFeatureGraphicalEditor.py` gelöscht (toter Re-Export-Shim, keine Caller) *(2026-06-08)*
|
||
- [x] `nodeDefinitions/__init__.py`: DEPRECATED-Label durch neutrales Shim-Label ersetzt *(2026-06-08)*
|
||
- [x] `routeWorkflowAutomation.py`: fehlender `DatabaseConnector`-Import ergänzt (Boot-Fix) *(2026-06-08)*
|
||
- [x] `routeWorkflowAutomation.py`: staler Import `modules.workflows.automation2.runEnvelope` → `modules.workflowAutomation.engine.runEnvelope` *(2026-06-08)*
|
||
- [x] `graphicalEditorRunFileLogger.py` → `runFileLogger.py` umbenannt + alle 4 Import-Stellen aktualisiert *(2026-06-08)*
|
||
- [x] `graphicalEditorDatabase` Alias in `interfaceWorkflowAutomation.py` entfernt (kein Caller) *(2026-06-08)*
|
||
- [x] Alle verbliebenen `graphicalEditor`-Kommentare/Docstrings in 7 Backend-Dateien neutralisiert *(2026-06-08)*
|
||
- [x] Demo-Configs: `graphicalEditor` aus Feature-Listen entfernt (`pwgDemo2026.py`, `investorDemo2026.py`) *(2026-06-08)*
|
||
- [x] Frontend: `console.error('[graphicalEditor]` → `'[workflowAutomation]` (6 Stellen), Kommentare aktualisiert *(2026-06-08)*
|
||
- [x] `_FEATURES_WITH_EDITOR`: `'graphicalEditor'` entfernt (WA ist kein Feature; nur `'workspace'` verbleibt) *(2026-06-08)*
|
||
- [x] Test-Dateien: `graphicalEditor` aus Parametrize-Listen und Assertions entfernt *(2026-06-08)*
|
||
- [x] UDB-Surface `'graphEditor'` → `'workflowAutomation'` (UnifiedDataBar.tsx, FilesTab.tsx, Automation2FlowEditor.tsx) *(2026-06-08)*
|
||
- [x] `Automation2FlowEditor.tsx`: 12 stale `instanceId`-Parameter aus API-Calls entfernt (API war bereits migriert) *(2026-06-08)*
|
||
- [x] `FilesTab.tsx`: `importWorkflowFromFile` Signatur korrigiert (stale `instanceId` entfernt) *(2026-06-08)*
|
||
- [x] Log-Prefixe `[automations]` → `[workflowAutomation]` in RunsTab.tsx + WorkflowsTab.tsx *(2026-06-08)*
|
||
- [x] Dead code entfernt: `WorkflowTasksPage.tsx` (942 LOC), `WorkflowTasks.module.css` *(2026-06-08)*
|
||
- [x] UDB generifiziert: `UdbSurface` → `string` (kein hardcoded Feature-Enum mehr); Workflow-Import-Logik aus `FilesTab.tsx` entfernt → Consumer (`Automation2FlowEditor`) uebernimmt via `onFileSelect`; `onWorkflowImportedFromFile` Prop entfernt *(2026-06-08)*
|
||
|
||
**Phase 0 — Scheduler-Boot entkoppeln (risikoarm, zuerst):**
|
||
- [x] Scheduler-Start in System-Lifespan (`app.py`, **nach** `setSchedulerMainLoop`/`eventManager.start()`); Poller bleibt **on-demand** (`ensureRunning`), `onStop`-Stop verlagert (Scheduler + Email-Poller in `app.py` Shutdown Schritt 3.5); GE-`onStart`/`onStop` **entfernt** *(2026-06-07)*
|
||
- [x] Routen-Registrierung via `loadFeatureMainModules()` weiterhin aktiv (Feature-Mantel noch da)
|
||
- [ ] Smoke: **bestehende (grandfathered)** geplante Runs feuern weiterhin, auch ohne GE-Feature-Instanz im Mandanten → manuell testen bei nächstem Deploy
|
||
|
||
**Phase 0.5 — Delaminierung (vor jedem Code-Umzug):**
|
||
- [x] ~~`portTypes`/`PORT_TYPE_CATALOG` delaminieren~~ — **erledigt** durch Refactoring (`datamodels/datamodelPortTypes.py`, L1)
|
||
- [x] ~~Kanonische Models nach L1~~ — **erledigt** (`datamodels/datamodelWorkflowAutomation.py`)
|
||
- [x] `PauseForHumanTaskError` + `PauseForEmailWaitError` → `datamodels/serviceExceptions.py` (kanonisch); Re-Export-Shim in `automation2/executors/inputExecutor.py`; Caller `methods/methodContext/actions/setContext.py` umgebogen *(2026-06-07)*
|
||
- [x] `coerceDocumentDataToBytes` + `_looksLikeAsciiBase64Payload` → `shared/documentUtils.py` (kanonisch); Re-Export-Shim in `automation2/executors/actionNodeExecutor.py`; Caller `methods/methodFile/actions/create.py` umgebogen *(2026-06-07)*
|
||
- [x] Guard-Test: `modules.workflows.{methods.methodContext.actions.setContext, methods.methodFile.actions.create}` importierbar **ohne** `modules.workflows.automation2.*` → bestätigt, kein `automation2`-Modul transitiv geladen *(2026-06-07)*
|
||
- [x] Node-Katalog (`STATIC_NODE_TYPES`, `features/graphicalEditor/nodeDefinitions/`): moved to `workflowAutomation/editor/nodeDefinitions/`; old `__init__.py` converted to re-export shim; cross-Caller `system/i18nBootSync.py` + `serviceAgent/workflowTools.py` work via shim *(2026-06-07)*
|
||
|
||
**Phase 1 — Mandatsweite API + System-RBAC (parallel zur Bestands-API):**
|
||
- [x] `AutoWorkflow.runAsPrincipal` in `datamodels/datamodelWorkflowAutomation.py` ergänzt (Optional[str], nullable, softFk → UserInDB)
|
||
- [x] System-RBAC + Nav-Block: `workflowAutomation` Section (order 25) mit 5 UI-Objekten (`ui.system.workflowAutomation.{workflows,editor,templates,runs,tasks}`) in `datamodelNavigation.py`; automatisch von `_buildUiObjectsFromNavigation` in RBAC-Katalog aufgenommen
|
||
- [x] Neue Routen `routeWorkflowAutomation.py`: `/api/workflow-automation/{workflows,versions,runs,tasks,steps}` (10 Endpunkte) mandatsweit; **Write-RBAC-Helper** `_validateWorkflowAccess(context, workflow, action)` (read = mandate-member, write/execute/delete = mandate-admin), `isPlatformAdmin`-Bypass; registriert in `app.py`
|
||
- [x] Scheduler **von `featureInstanceId` entkoppelt**: `if not instanceId`-Guard in `mainScheduler._syncScheduledWorkflows` entfernt; `instanceId` default `""` statt skip; Bestand läuft weiter
|
||
- [x] Runtime-Mandatsvalidierung beim `FeatureInstanceRef`-Auflösen: `_validateFeatureInstanceMandates()` in `executionEngine.py` nach `materializeFeatureInstanceRefs()` — defence-in-depth warning-log bei cross-mandate-Referenzen (A0.2)
|
||
|
||
**Phase 2 — UI «Workflow-Automation»-Gruppe:**
|
||
- [x] Nav-Block `workflowAutomation` in `datamodels/datamodelNavigation.py` (`NAVIGATION_SECTIONS`) — bereits in Phase 1 erledigt
|
||
- [x] `/workflow-automation`-Route + `WorkflowAutomationPage.tsx` Hub-Seite mit Tabs (Workflows · Läufe · Tasks), `?tab=`-Deeplinks; Import in `App.tsx`
|
||
- [x] Icons in `pageRegistry.tsx` für `page.system.workflowAutomation.*` registriert (FaSitemap, FaProjectDiagram, FaCopy, FaPlay, FaTasks)
|
||
- [x] `MandateNavigation.tsx`: `extraStaticBlocks`-Logik — unbekannte statische Blöcke werden als eigene Nav-Sektionen gerendert (nicht mehr in System-Block gemergt)
|
||
- [x] Kontext-Selector (Alle Mandanten / Mandant X) im Hub — BillingDataView-Pattern; `selectedMandateId` an alle Tabs *(2026-06-08)*
|
||
- [x] Editor-Entkopplung: `mandateId` ist primaerer Kontext; `instanceId` nur noch Access-Gate fuer Workspace-Datasource-APIs *(2026-06-08)*
|
||
- [x] WorkflowsTab/RunsTab: Server-seitige Mandantenfilterung via `mandateId`-Query-Param *(2026-06-08)*
|
||
- [x] TasksTab im Hub verdrahtet (neue API `/api/workflow-automation/tasks`) *(2026-06-08)*
|
||
- [x] WorkflowsTab Edit-Navigation korrigiert: `/workflow-automation?tab=editor&workflowId=...` (statt mandate-scoped Feature-Route) *(2026-06-08)*
|
||
- [x] `_FEATURES_WITH_EDITOR`: `'workflowAutomation'` entfernt (ist kein Feature) *(2026-06-08)*
|
||
- [x] Dead code entfernt: `WorkflowTasksPage.tsx` (942 LOC, instance-scoped legacy), `WorkflowTasks.module.css` *(2026-06-08)*
|
||
- Keep-Alive fuer Editor-Tab: separate Iteration (aktuell wird der gesamte Hub kept-alive)
|
||
|
||
**Phase 3 — DB & Datenmigration:**
|
||
- [x] `AutoWorkflow.featureInstanceId` → `Optional[str]` (default=None, softFk=True); Pydantic-only, **kein DDL** — Spalte bereits DB-nullable
|
||
- [x] Cascade-Delete: `_cascadeDeleteAutoWorkflow` in `routeWorkflowDashboard.py` ist bereits mandatsweit (löscht nach workflowId, unabhängig von featureInstanceId)
|
||
|
||
**Phase 4 — Feature-Mantel deprecaten (nicht entfernen — Koexistenz):**
|
||
- [x] GE-Hardcode in `routeSystem.py` als DEPRECATED markiert (bleibt für bestehende Feature-Instanzen)
|
||
- [x] Import-Umstellung: `routeWorkflowDashboard.py`, `routeAutomationWorkspace.py`, `routeSystem.py` importieren Models aus kanonischer `datamodels.datamodelWorkflowAutomation` statt aus `features.graphicalEditor.datamodelFeatureGraphicalEditor`
|
||
- Bestehende Feature-Routen `/api/workflows/{instanceId}/…` bleiben aktiv (Koexistenz mit neuer mandatsweiter API)
|
||
- Feature-Registration (`getFeatureDefinition`, `UI_OBJECTS`, Store) bleibt — wird erst bei vollständiger Migration entfernt
|
||
|
||
**Abschluss:**
|
||
- [x] `_CHANGELOG.md` aktualisiert
|
||
|
||
## 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?~~ **Entschieden (2026-06-08):** `/api/workflow-automation/…` ist der kanonische Pfad. `routeWorkflowAutomation.py` registriert alle Endpunkte dort. `/api/system/workflow-runs` bleibt als Legacy-Alias (Dashboard).
|
||
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/workflowAutomation/{editor,engine,scheduler}/`, `modules/datamodels/{datamodelPortTypes,datamodelWorkflowAutomation,datamodelNavigation}.py`, `modules/routes/{routeWorkflowAutomation,routeWorkflowDashboard,routeAutomationWorkspace,routeSystem}.py`, `app.py`
|
||
- Import-/Layer-Stand (Refactoring 2026-06-05/06, inkl. Deferred-Liste für diese Komponente): `local/notes/refernce-analysis/platform-core-import-analyse.md`
|
||
- 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
|
||
|
||
- [x] `b-reference/platform/workflowAutomation.md`: Kanon-Seite aktualisiert (Code-Pfade, API, RBAC, Navigation) *(2026-06-08)*
|
||
- [x] `.cursor/rules/rbac-role-separation.mdc` + `feature-instance-scoping.mdc`: WorkflowAutomation als dokumentierte Ausnahme eingetragen *(2026-06-08)*
|
||
- [x] `topics-todo.md` aktualisiert (WorkflowAutomation = System-Komponente, DONE) *(2026-06-08)*
|
||
- [x] `features/graphicalEditor/` Verzeichnis komplett gelöscht (keine Dateien, keine Caller) *(2026-06-08)*
|
||
- [x] **Cross-Import Assessment**:
|
||
- [x] `workflows -> features.graphicalEditor` (~40 Imports): eliminiert — alle Imports auf `modules.workflowAutomation.*` *(2026-06-08)*
|
||
- [x] `serviceCenter -> features.graphicalEditor` (5x workflowTools): eliminiert — kanonische Pfade *(2026-06-08)*
|
||
- [x] `interfaces/interfaceDbManagement.py:936` -> `workflowArtifactVisibility`: erledigt — `modules.workflowAutomation.engine.workflowArtifactVisibility` *(2026-06-08)*
|
||
- [x] Re-Export-Shims: `datamodelFeatureGraphicalEditor.py` gelöscht; `stateTools.py` Shim bleibt (7 Caller via `processing/shared/`, nicht Teil dieser Migration) *(2026-06-08)*
|
||
- [x] Keine `from modules.features.graphicalEditor` oder `from modules.workflows.automation2` Imports mehr im gesamten Codebase (verifiziert via grep) *(2026-06-08)*
|
||
- [ ] Lazy Imports in `workflows/` reduzieren (separates Refactoring-Thema, nicht Teil dieser Migration)
|
||
- [ ] Dieses Dokument → `4-done/` verschieben
|