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

33 KiB
Raw Blame History

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: automation2workflowAutomation/engine/, schedulerworkflowAutomation/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.pyNAVIGATION_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.pyTABLE_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 getGraphicalEditorServicesfeatureCode/featureInstanceId im Hub: Billing per-Node auf berührte Instanz (A0.2, Executor-Change), Mandate = mandateId.
    • serviceCenter/.../toolboxRegistry.pyToolboxDefinition(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.tsxblock.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.tsxgraphicalEditor 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.featureInstanceIdOptional (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.
  • 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/