33 KiB
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.mdA0.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-TokenWorkflowAutomation/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/AutoRunsind Orchestrierungs-Metadaten (eigene DBpoweron_graphicaleditor). - Einziges Feature mit
onStart/onStop— bootet beim App-Start den globalen Scheduler (Email-Poller startet on-demand viaemailPoller.ensureRunning, gestoppt inonStop). - Scheduler/Engine liegen ohnehin unter
modules/workflows/(System), nicht im Feature. getWorkflows()filtert nur nachmandateId(«cross-instance»); ein Graph referenziert legitim mehrere Features/Instanzen über per-NodeFeatureInstanceRef.- 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. featureInstanceIdhat heute zwei Bedeutungen: (a) GE-Owner/RBAC-Scope aufAutoWorkflow, (b) Daten-Scope der Ausführung (targetFeatureInstanceId+ per-NodeFeatureInstanceRef). Nur (a) wird abgebaut; (b) bleibt — es ist die Cross-Feature-Referenz.- Kaum DDL nötig (Code-verifiziert). Tabellen werden ohne
NOT NULL(ausserid-PK) und ohne DB-FKs angelegt (connectorDbPostgre.py_create_table_from_model); FKs sind app-level (fkRegistry,app.pyvalidateFkTargets). Heisst:featureInstanceIdist schon jetzt DB-nullable → «nullable machen» ist nurOptional[str]im Pydantic-Modell (datamodelFeatureGraphicalEditor.py) + Query-/Code-Anpassungen, keine Migration.runAsPrincipal= additivesADD COLUMN(von_ensureTableExistsabgedeckt). Echte Arbeit = Application-Logik (Routen/Queries, diefeatureInstanceIdvoraussetzen), nicht Schema. - Bestehende Daten grandfathern.
AutoWorkflow-Zeilen tragenfeatureInstanceId;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 inrouteFeatureGraphicalEditor.pysind 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) inautomation2.executors, werden aber vonmethods/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:
graphicalEditorals System-KomponenteWorkflowAutomationfü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ührteFeatureInstanceRefin denrecordUsage-Kontext threaden) — kein Graph-/Semantik-Umbau. - Explizit NICHT: den geteilten Workflow-Execution-Layer (
workflows/methods/,workflows/processing/) in die Komponente ziehen — er bleibt untermodules/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_graphicaleditorbleibt). - Explizit NICHT: bestehende «Automation»-Code-Namen umbenennen (
AutomationsDashboardPage,routeAutomationWorkspace,workflows/automation2) — nur die neue Komponente trägt das TokenWorkflowAutomation.
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 (neuworkflows/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_CATALOGund der Node-Katalog/STATIC_NODE_TYPESliegen heute untergraphicalEditor/, werden aber vonmethods/methodBase.py,processing/shared/parameterValidation.py,serviceAgent/actionToolAdapter.pyundshared/i18nRegistry.pygenutzt → bleiben geteilt (workflows/workflowContracts/); die Komponente konsumiert sie.- Korrektur eines früheren Fehlers: «
automation2wird von Chats nicht importiert» war falsch.methods/methodFile/actions/create.py+methods/methodContext/actions/setContext.pyimportierenautomation2.executors(_coerce_document_data_to_bytes,PauseForHumanTaskError) top-level — geladen viamethodDiscoveryim 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.serviceCenterimportierbar sind, ohnemodules.workflowAutomation.*zu laden. Migration nur der Automation-Teile (Re-Export/Shims; ~20 Importer vonworkflows.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 (MusterAutomationsDashboardPage). - Abgrenzung zur kundenseitigen «Lösungen»-Surface: Der
SolutionsTabhier ist die Admin/Power-User-Sicht über alle Solutions; die kundenseitigeSolutionsView(L4) bleibt pro Host-Feature eingebettet (Trustee usw.) und zeigt nur die für diesen Kontext konfigurierten Solutions. Beide nutzenapi/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 globalemevent-Sysadmin (A0.1).- Delaminierung zuerst (Phase 0.5):
PauseForHumanTaskError+_coerce_document_data_to_bytesausautomation2/executors/in den Shared-Layer;portTypes/Node-Katalog (STATIC_NODE_TYPES) ausgraphicalEditor/nachworkflows/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 nachmodules/workflowAutomation/editor/führen;mainGraphicalEditor.py:getFeatureDefinition/TEMPLATE_ROLES/onStartentfernen bzw. zu System-Registrierung umbauen (instantiable=False, VorbildmainSystem.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._validateTargetInstancezusätzlich um Mandatsgrenze ergänzen (AC8, heute nurFeatureAccess).modules/routes/routeSystem.py— GE-Hardcode in_getFeatureUiObjectsentfernen; neuen statischen Nav-BlockworkflowAutomationausliefern.modules/system/mainSystem.py—NAVIGATION_SECTIONS: Blockid="workflowAutomation"(order ~25) +ui.system.workflowAutomation.*-Objekte; Store-Eintragresource.store.graphicalEditorentfernen.modules/interfaces/interfaceFeatures.py—_copyTemplateWorkflowslä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 denlauncher/-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+ LegacyAutomation2*): Option B — Namespacefeature.graphicalEditorbeibehalten + System-DATA-Objekte ergänzen. (Ein Flip aufsystem.workflowAutomationverwaist bestehendeAccessRule/Rollen-Zeilen; nur mit zwingend gekoppeltem Migrations-Skript im selben Deploy.)modules/interfaces/interfaceBootstrap.py— Store-Grant + ggf. neue System-RollenworkflowAutomation-*.modules/features/graphicalEditor/mainGraphicalEditor.pygetGraphicalEditorServices—featureCode/featureInstanceIdim 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(ziehtSTATIC_NODE_TYPES/PORT_TYPE_CATALOG→ nach Delaminierung aufworkflowContractsumbiegen),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 (analogadmin).src/App.tsx—/workflow-automation/*-Routen (Hub +?tab=Deeplinks); Redirects der alten/mandates/.../graphicalEditor/.../*.src/pages/AutomationsDashboardPage.tsx→ inWorkflowAutomationHubPagemit Tabs überführen (Workflows, Läufe, Editor, Vorlagen, Tasks).src/pages/FeatureView.tsx—graphicalEditorausVIEW_COMPONENTSentfernen.src/pages/views/graphicalEditor/*— vonuseInstanceId()/useCurrentInstance()auf expliziten Mandanten-/Instanz-Picker (Scope-Selector) umstellen.src/config/keepAliveRoutes.tsx— Editor-Keep-Alive-Pfad auf/workflow-automation/editorumziehen.src/config/pageRegistry.tsx— Icons fürpage.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= additivesADD COLUMN;featureInstanceIdist bereits DB-nullable (kein DDL, keine DB-FK; nur PydanticOptional+ 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, nachsetSchedulerMainLoop;eventUserist dort schon vorhanden); Poller bleibt on-demand (ensureRunning), nuronStop-Stop verlagern; GE-onStart/onStopentfernen - 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_bytesausautomation2/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,actionToolAdapterdarauf umbiegen- Guard-Test:
modules.workflows.{methods,processing}+serviceCenterimportieren ohnemodules.workflowAutomation.*
Phase 1 — Mandatsweite API + System-RBAC (parallel zur Bestands-API):
AutoWorkflow.runAsPrincipalergänzen (additivesADD 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
featureInstanceIdentkoppeln:if not instanceId-Guard (mainScheduler.py) entfernen, Interface/Services nicht mehr instanz-gekeyt auflösen, Ausführung übertargetFeatureInstanceId/Mandant scopen - Per-Node-Billing: berührte
FeatureInstanceRefin denrecordUsage-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
workflowAutomationinmainSystem.py+ Handling inMandateNavigation.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;validateFkTargetstolerantSolution-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),targetFeatureInstanceIderhalten - Mandate-Delete-Cascade mandatsweit (Vorbild
_cascadeDeleteAutoWorkflow)
Phase 4 — Feature-Mantel entfernen:
getFeatureDefinition/registerFeature/TEMPLATE_ROLES/UI_OBJECTS(feature)/Store-Eintrag entfernen; GE-Hardcode inrouteSystem.pyrausFeatureView.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-referenceaktualisieren;_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
- 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-RolleworkflowAutomation-user, oder genügt Mandats-user? (Feature-Instanz-Rollen sind keine Option mehr —instantiable=False+rbac-role-separation.mdc.) - API-Pfad:
/api/workflow-automation/…neu vs. bestehendes/api/system/workflow-runsausbauen — konsolidieren? - Run-as-Principal-Verwaltung: Ersteller-Identität vs. dedizierter Service-Principal (siehe CustomerCases offene Frage #1).
- Alt-URLs: wie lange Redirects der
{instanceId}-Pfade halten (Bookmarks/Deeplinks)? - Datei-/Doku-Rename: dieses Plan-Dokument ggf. auf
…-workflowautomation-system-component.mdumbenennen (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.mdcaktualisieren (WorkflowAutomation als dokumentierte Ausnahme; stale Globsgateway//frontend_nyla/→platform-core//ui-nyla/korrigieren)TOPICS.mdaktualisieren (WorkflowAutomation = System-Komponente)- Dieses Dokument →
c-work/2-build/bei Annahme, dann4-done/