19 KiB
Trustee Workflow-Audit (A1) & Generischer Workflow-Run-Workspace (A2)
Beschreibung und Kontext
Der User klickt im Trustee-Dashboard Service-Karten an (Budget-Vergleich,
KPI-Dashboard, Cashflow, Forecast, Jahresabschluss-Pruefung) und landet auf
Tabs in TrusteeAnalyseView / TrusteeAbschlussView, wo der jeweilige
Workflow gestartet wird.
Zwei Themen:
- A1 Audit: Sind diese Workflows wirklich auf dem aktuellen Pick-not-Push / Typed-Action-Stack? -- JA, alle GREEN (siehe Befund unten). Hier wird nur dokumentiert.
- A2 Result-Sichtbarkeit: Wenn der User waehrend des Runs die Seite
wechselt, ist das Resultat (KI-Antwort + generierte Files) anschliessend
weg -- es lebt nur im React-State der Source-View. Persistenz existiert
zwar in
AutoRun/AutoStepLog, ist aber nirgends als User-UI erschlossen.
Geschaeftstreiber: UX-Bruch ("ich war kurz auf einer anderen Seite, jetzt ist mein Report weg") + fehlende Single-Source-of-Truth fuer Workflow- Outputs. Plus: Workflow muss eine klare Ziel-Feature-Instanz haben, damit Datenquellen, RBAC und Workspace-Filterung sauber funktionieren.
Architekturentscheidung: GraphicalEditor bleibt Feature
Der GraphicalEditor (GE) ist konzeptuell eine Plattform-Capability (nicht
ein Domain-Feature wie Trustee). Er bleibt aber aus pragmatischen Gruenden
als Feature registriert (isolierte DB, Feature-Roles, Template-Kopier-
Mechanismus). Die URL-Struktur /api/workflows/{geInstanceId}/... nutzt
die GE-Instanz als RBAC-Scope/Eigentuemer.
Neues Konzept: targetFeatureInstanceId -- das Feld am Workflow, das
bestimmt welche Daten (Dateien, Connections, Domain-Objekte) zur Verfuegung
stehen. Dies ist NICHT die GE-Instanz (Eigentuemer), sondern die
Ziel-Feature-Instanz (Daten-Scope bei Execution).
Kein Multi-Instance: Ein Workflow = Ein Ziel-Scope
- ALLE Nodes eines Workflows operieren auf derselben
targetFeatureInstanceId. - Kein per-Node Override -- Engine resolved
{{featureInstanceId}}zentral aus dem Workflow-Level-Feld. - Wenn Daten aus einer anderen Instanz benoetigt werden: diese Daten im Ziel-Scope bereitstellen (z.B. via Data-Connection, File-Import), NICHT einen Multi-Instance-Workflow bauen.
- RBAC bei Execution: ein Check auf
targetFeatureInstanceIdreicht.
targetFeatureInstanceId kann auch die GE-Instanz selbst sein
Fuer generische Workflows ohne Domain-Daten (z.B. "AI verarbeitet Eingabetext und generiert Report") waehlt der User die GE-Instanz selbst ("Automation allgemein") als Ziel. Dann stehen nur Files/Connections zur Verfuegung, die direkt an der GE-Instanz haengen.
Fokus und kritische Details
POST /api/workflows/{instanceId}/executeist heute synchron (await executeGraph(...), kein BackgroundTask). Der Workspace muss den Request nicht aendern -- er macht nur die persistiertenAutoRun-Daten auffindbar.AutoWorkflow.featureInstanceId= GE-Instanz (Eigentuemer). NICHT anfassen. Neues FeldtargetFeatureInstanceIddaneben. Hinweis: Pydantic-Model hatfeatureInstanceId: str(required, nicht Optional), aber DB-Spalte istnullable=YES. System-Templates setzenfeatureInstanceId=""oder ueberspringen die Validation.- System-Templates (
isTemplate=True) habentargetFeatureInstanceId=NULL(Template hat kein konkretes Ziel, erst die Kopie). - Scheduler bindet
targetFeatureInstanceIdvom persistierten Workflow -- kein Override moeglich. - Tab-Position: User wuenscht Workspace explizit unter
/automations(Seite "Nutzung > Automation") als zusaetzlichen Tab neben Dashboard + Workflows. AutoRunhat heute KEINfeatureInstanceId-Feld -- Instanz-Zuordnung laeuft indirekt ueberAutoRun.workflowId -> AutoWorkflow.targetFeatureInstanceId.
Ziel und Nicht-Ziele
- Ziel A1: Audit-Befund GREEN dokumentiert (Wiki-Update).
- Ziel A2: Generische Workflow-Run-Workspace-View; jeder Workflow (non-
template) hat Pflicht-
targetFeatureInstanceId; TrusteeAnalyseView verweist statt Inline-Result auf den Workspace. - NICHT: Async-Umstellung des Execute-Endpoints.
- NICHT: Browser-Push-Notification.
- NICHT: De-Featuring des GraphicalEditors (bewusster pragmatischer Kompromiss, siehe Architekturentscheidung).
- NICHT: Multi-Instance-Workflows.
Betroffene Module
- Gateway:
platform-core/modules/features/graphicalEditor/datamodelFeatureGraphicalEditor.py(neues FeldAutoWorkflow.targetFeatureInstanceId).platform-core/modules/features/graphicalEditor/routeFeatureGraphicalEditor.py(Save-Validation: non-template brauchttargetFeatureInstanceId).platform-core/modules/features/graphicalEditor/interfaceFeatureGraphicalEditor.py(createWorkflow/updateWorkflow: Feld durchreichen).platform-core/modules/workflows/automation2/executionEngine.py(executeGraph:{{featureInstanceId}}austargetFeatureInstanceIdresolven).platform-core/modules/workflows/scheduler/mainScheduler.py(Schedule-Fire nutzttargetFeatureInstanceIdvom Workflow).platform-core/modules/features/trustee/mainTrustee.py(Templates pruefen:targetFeatureInstanceIdwird bei_copyTemplateWorkflowsgesetzt).platform-core/modules/interfaces/interfaceFeatures.py(_copyTemplateWorkflows:targetFeatureInstanceIdauf Ziel-Instanz setzen bei Kopie).- Neue Route-Datei:
platform-core/modules/routes/routeAutomationWorkspace.py(User-facing/api/automations/runs/...Endpoints).
- Frontend:
- Neuer Tab in
ui-nyla/src/pages/AutomationsDashboardPage.tsx. - Neue Komponenten
WorkflowRunWorkspaceView,WorkflowRunDetailView. - FlowEditor CanvasHeader: Pflicht-Dropdown "Ziel-Instanz"
(
targetFeatureInstanceId). TrusteeAnalyseView.tsx: Inline-Result-Anzeige raus, Link zum Workspace-Detail.TrusteeAbschlussView.tsx: analog -- Link zum Workspace-Detail.
- Neuer Tab in
- DB-Migration:
AutoWorkflowbekommt neue SpaltetargetFeatureInstanceId(nullable, wegen Templates). Bestand: Dev-Audit zeigt 0 non-template Workflows ohne Instance (alle 24 regulaeren Workflows haben bereitsfeatureInstanceIdaus dem URL- Context). Migration: fuer BestandtargetFeatureInstanceId := featureInstanceIdsetzen (selbe Instanz wie GE = Default fuer alte generische Workflows ODER aus Graph-Nodes extrahieren wenn dort konkreter Wert steht). - RBAC: Neue Endpoints pruefen
targetFeatureInstanceIdgegen User- Berechtigungen via FeatureAccess.
Befund A1 (Audit) -- bereits GREEN
| Service | Workflow-ID | Backend-Definition | Status |
|---|---|---|---|
| Budget-Vergleich | trustee-budget-comparison |
mainTrustee.py ~430-461 |
GREEN |
| KPI-Dashboard | trustee-kpi-dashboard |
mainTrustee.py ~463-478 |
GREEN |
| Cashflow-Rechnung | trustee-cashflow |
mainTrustee.py ~480-492 |
GREEN |
| Prognose | trustee-forecast |
mainTrustee.py ~494-507 |
GREEN |
| Jahresabschluss-Pruefung | trustee-year-end-check |
mainTrustee.py ~509-522 |
GREEN |
Engine-Pipeline: executeGraph (in workflows/automation2/executionEngine.py
Z. ~305-350) ruft materializeFeatureInstanceRefs (typed-ref envelopes,
NICHT Placeholder-Substitution) + materializeConnectionRefs +
validateGraph vor jedem Lauf auf.
ACHTUNG: {{featureInstanceId}}-Placeholder werden heute NUR in
_copyTemplateWorkflows pre-baked (graphJson.replace(...), Z. ~336-338
in interfaceFeatures.py). executeGraph selbst hat KEINE
Placeholder-Substitution -- diese muss NEU gebaut werden (Phase 1).
Zusaetzlich zu den 5 Analyse-Workflows existieren noch 2 weitere
Trustee-Templates (trustee-receipt-import, trustee-sync-accounting)
in TEMPLATE_WORKFLOWS. Diese verwenden ebenfalls
{{featureInstanceId}}-Placeholders und sind GREEN.
Entscheidungen
| Datum | Entscheidung | Begruendung |
|---|---|---|
| 2026-04-29 | GE bleibt Feature, neues Feld targetFeatureInstanceId |
De-Featuring zu teuer; targetFeatureInstanceId loest das eigentliche Problem sauber |
| 2026-04-29 | Kein Multi-Instance: 1 Workflow = 1 Ziel-Scope | Einfach, klar, RBAC auf Workflow-Ebene pruefbar; bei Bedarf Daten im Ziel-Scope bereitstellen |
| 2026-04-29 | targetFeatureInstanceId kann auch die GE-Instanz selbst sein |
Generische Workflows ohne Domain-Daten brauchen einen Scope |
| 2026-04-29 | Workspace ist GENERISCH plattformweit, nicht Trustee-spezifisch | Doppelt-Bauen vermeiden; alle Features profitieren |
| 2026-04-29 | Workspace lebt unter /automations als Tab "Workspace" |
User-Vorgabe; Nutzungspfad "Nutzung > Automation > Workspace" |
| 2026-04-29 | Neuer Endpoint /api/automations/runs/... statt Erweiterung von System-Dashboard |
Klare Trennung User-Workspace (RBAC-gefiltert) vs. Admin-Dashboard |
| 2026-04-29 | Bestand-Migration: targetFeatureInstanceId := featureInstanceId (GE-Instanz) |
Dev-Audit: 0 non-template ohne Instance; 24 regulaere haben GE-Instance aus URL |
| 2026-04-29 | TrusteeAnalyseView: Inline-Result raus, Workspace-Link rein | Single Source of Truth im Workspace; kein verlorener State bei Seitenwechsel |
| 2026-04-29 | Kein interaktives Migrations-Skript noetig | Dev-Audit: 0 echte Workflows betroffen (nur 2 System-Templates, die NULL behalten duerfen) |
Umsetzungs-Checkliste
Phase 1 -- targetFeatureInstanceId + Validation
datamodelFeatureGraphicalEditor.py:targetFeatureInstanceId: Optional[str] = Field(default=None, ...)(nullable wegen Templates).interfaceFeatureGraphicalEditor.pycreateWorkflow: aus Body oder Fallbackself.featureInstanceIduebernehmen.interfaceFeatureGraphicalEditor.pyupdateWorkflow: Feld im Update-Payload erlauben (NICHT strippen wiefeatureInstanceId).routeFeatureGraphicalEditor.pySave-Endpoint: wennisTemplate=FalseundtargetFeatureInstanceIdfehlt/leer -> 400.routeFeatureGraphicalEditor.pyExecute-Endpoint: Vorab-ChecktargetFeatureInstanceIdvorhanden, User hat FeatureAccess darauf.executionEngine.pyexecuteGraph: NEU BAUEN -- VORmaterializeFeatureInstanceRefseine Placeholder-Substitution einfuegen: alle{{featureInstanceId}}-Vorkommen im serialisierten Graph-JSON durchtargetFeatureInstanceId(uebergeben als neuer Parameter) ersetzen. Bestehende hart-kodierte UUIDs in Nodes bleiben unangetastet (Backward-Compat fuer kopierte Templates, wo_copyTemplateWorkflowsbereits pre-baked hat). Hinweis:materializeFeatureInstanceRefsmacht typed-ref-envelope- Rewriting, NICHT Placeholder-Substitution -- beides ist noetig.mainScheduler.py: Schedule-Fire liest heuteworkflow["featureInstanceId"](= GE-Instanz) und uebergibt es alsinstanceIdanexecuteGraph. Aendern: zusaetzlichworkflow["targetFeatureInstanceId"]lesen und als neuen ParametertargetFeatureInstanceIdanexecuteGraphuebergeben (fuer die Placeholder-Substitution).instanceId(GE-Instanz) bleibt fuer RBAC-Scope bestehen.interfaceFeatures.py_copyTemplateWorkflows: bei KopietargetFeatureInstanceId = instanceId(die Ziel-Feature-Instanz) EXPLIZIT imcreateWorkflow-Payload setzen. Hinweis: heute wirdfeatureInstanceIdNICHT im Payload gesetzt, sondern kommt implizit aus dem GE-Interface-Context (getGraphicalEditorInterface(..., instanceId)).targetFeatureInstanceIdmuss explizit uebergeben werden. In-Graph-Placeholders werden wie bisher auch pre-baked (graphJson.replace("{{featureInstanceId}}", instanceId)).- Bestand-Migration (einmalig, idempotent):
UPDATE "AutoWorkflow" SET "targetFeatureInstanceId" = "featureInstanceId" WHERE "targetFeatureInstanceId" IS NULL AND "isTemplate" IS NOT TRUE(kann als Boot-Telemetrie oder im Audit-Skript laufen). - Unit-Test: Workflow-Save ohne
targetFeatureInstanceId-> 400. - Unit-Test: Execute mit
targetFeatureInstanceIddas User nicht zugreifen darf -> 403.
Phase 2 -- FlowEditor Toolbar Dropdown
- CanvasHeader: neues Dropdown "Ziel-Instanz" (Pflicht fuer non- template). Optionen: alle FeatureInstances des Mandats (inkl. GE- Instanz als "Automation allgemein"), gefiltert nach User-FeatureAccess.
- Bei Wechsel: Workflow-Update mit neuem
targetFeatureInstanceId, Datenquellen-Browser (SourcesTab, UDB-Listen) refreshen auf neuen Scope. - Wenn
targetFeatureInstanceIdleer (z.B. frisch aus Template): User MUSS erst Instanz waehlen bevor Save/Run moeglich.
Phase 3 -- Generischer WorkflowRunWorkspace
- Neue Route-Datei
platform-core/modules/routes/routeAutomationWorkspace.py: -GET /api/automations/runs(Query-Params:scope=mine|mandate,status,targetInstanceId,workflowId,limit,offset). RBAC: nur Runs sichtbar wo User FeatureAccess auftargetFeatureInstanceIdhat. -GET /api/automations/runs/{runId}/detail-- kombiniert AutoRun + AutoStepLog + verlinkte FileItems. - Tab "Workspace" in
AutomationsDashboardPage.tsx(neben Dashboard, Workflows). - Komponente
WorkflowRunWorkspaceView: Liste mit Filtern (Status, Workflow-Name, Ziel-Instanz, Zeitraum), Pagination. - Komponente
WorkflowRunDetailView: Chat-aehnliche Ansicht - Header: Workflow-Name, Status, Start/Ende, Ziel-Instanz. - Eingabe-Bubble: Trigger-Payload (formatiert). - Step-Bubbles: chronologisch, pro Step-Output kollabierbar. - Final-Bubble: KI-Antwort als Markdown. - Documents-Sektion: alle generierten FileItems als Karten mit Direkt-Download.
Phase 4 -- Trustee-Views Refactor + Notifications
TrusteeAnalyseView:resultText/resultDocumentsState (Z. ~124-125) und die zugehoerige Output-Extraktion (Z. ~202-253) raus. Nach Run-Ende: Navigation/Link zum Workspace-Detail (runId).TrusteeAbschlussView: hat KEINresultText/resultDocuments-- zeigt nur Status/runSummary/runError. Aenderung: nach Run-Ende zusaetzlich Link zum Workspace-Detail einfuegen (fuer die Detail-Ansicht der Step-Outputs).- Toast bei Run-Ende: erweitern um Klick-Action zum Detail-View.
- Sidebar-Badge auf "Automation" (Counter "neu seit letztem Besuch",
localStorage-basiert).
Akzeptanzkriterien
| # | Kriterium (Given-When-Then) | Prio |
|---|---|---|
| 1 | Given non-template Workflow ohne targetFeatureInstanceId, When Save, Then 400 |
must |
| 2 | Given Workflow mit targetFeatureInstanceId, When User keinen FeatureAccess auf diese Instanz hat, When Execute, Then 403 |
must |
| 3 | Given Trustee-Run gestartet, When User Seite wechselt und zu Tab "Workspace" geht, Then Run mit allen Outputs sichtbar | must |
| 4 | Given Run mit generiertem File, When User auf Document-Karte klickt, Then File wird direkt heruntergeladen | must |
| 5 | Given FlowEditor offen, When User "Ziel-Instanz" waehlt, Then Datenquellen-Browser zeigt nur Daten dieser Instanz | must |
| 6 | Given Run-Ende-Toast, When User klickt, Then Navigation direkt zum WorkflowRunDetailView | should |
| 7 | Given neue Runs seit letztem Besuch, When User Sidebar sieht, Then "Automation" hat Counter-Badge | should |
| 8 | Given Template-Workflow kopiert, When Kopie in Mandate entsteht, Then targetFeatureInstanceId automatisch auf Ziel-Instanz gesetzt |
must |
Testplan
| ID | AC | Art | Automatisiert | Repo-Pfad | Status |
|---|---|---|---|---|---|
| T1 | 1 | api | ja | platform-core/tests/features/graphicalEditor/test_workflow_save_target_instance.py | pending |
| T2 | 2 | api | ja | platform-core/tests/features/graphicalEditor/test_workflow_execute_rbac.py | pending |
| T3 | 3,4 | e2e | ja | ui-nyla/tests/e2e/workflow-run-workspace.spec.ts | pending |
| T4 | 5 | unit | ja | ui-nyla/src/components/flowEditor/tests/TargetInstanceDropdown.test.tsx | pending |
| T5 | 8 | api | ja | platform-core/tests/features/test_copy_template_workflows.py | pending |
Dev-DB-Befund (2026-04-29)
- DB:
poweron_graphicaleditor, TabelleAutoWorkflow - Spalte
featureInstanceId:text, nullable=YES (kein NOT NULL) - Total: 26 Workflows (24 regulaer, 2 System-Templates)
- 0 regulaere Workflows ohne
featureInstanceId-> KEINE Daten-Migration noetig - 2 System-Templates (
isTemplate=True,templateScope='system'):featureInstanceId=NULL,mandateId=NULL-> korrekt, bleiben NULL - 2 orphan
AutoRun-Rows (workflowId='transient-XXX') -> Test-Reste, zu loeschen - Bestand-Migration:
targetFeatureInstanceId := featureInstanceIdfuer alle non-template Rows (= GE-Instanz als initialer Default, da diese Workflows generisch sind bzw. Graph-intern bereits konkrete Instance- UUIDs haben)
Risiken und Mitigationen
| Risiko | Impact | Mitigation |
|---|---|---|
executeGraph Placeholder-Substitution aendert Graph-Semantik fuer bestehende Workflows |
hoch | Pre-baked Templates haben bereits konkrete UUIDs -- Substitution greift nur bei {{featureInstanceId}}-Literals, die in kopierten Workflows nicht mehr vorkommen. Unit-Test mit beiden Faellen (pre-baked + Placeholder). |
Bestand-Migration setzt targetFeatureInstanceId := featureInstanceId (GE-Instanz), aber manche kopierten Trustee-Workflows haben die echte Trustee-Instanz nur in den Graph-Nodes (pre-baked) |
mittel | Fuer Bestand ist das OK: die pre-baked Nodes funktionieren weiterhin, da executeGraph existierende UUIDs nicht ueberschreibt. Nur neue Runs benoetigen korrekte targetFeatureInstanceId. User kann im FlowEditor nachtraeglich aendern. |
Neue RBAC-Pruefung (targetFeatureInstanceId) koennte bestehende Execute-Calls brechen wenn User keinen expliziten FeatureAccess auf Ziel-Instanz hat |
mittel | Audit-Query vor Release: alle non-template Workflows pruefen ob ownerId FeatureAccess auf targetFeatureInstanceId hat. Ggf. Soft-Rollout mit Warning statt 403 fuer 1 Sprint. |
Pydantic featureInstanceId: str (required) vs DB nullable -- Templates koennten Save-Fehler erzeugen |
niedrig | System-Templates werden im Code erzeugt, nicht via API. Pydantic-Validation wird bei API-Save erzwungen, bei internem createWorkflow umgangen. Bestehendes Verhalten. |
Links
- Audit-Quelle: Subagent-Report + DB-Query 2026-04-29.
- Code-Cross-Check: Subagent 2026-04-29 (16 Annahmen verifiziert).
- Wiki:
wiki/b-reference/platform-core/workflow.md,wiki/b-reference/platform-core/features/trustee.md.
Abschluss
wiki/b-reference/platform-core/workflow.mdAbschnitt "targetFeatureInstanceId + Workspace" anlegenwiki/b-reference/platform-core/features/trustee.mdResult-UX-Sektion aktualisierenwiki/TOPICS.mdggf. Tab-Beschreibung- Dieses Dokument ->
4-done/verschoben