From 5955caff61960e2062c0171f9add088eb6e5ec4b Mon Sep 17 00:00:00 2001
From: ValueOn AG
Date: Wed, 29 Apr 2026 21:27:11 +0200
Subject: [PATCH] plana+c implemented
---
...rustee-workflow-audit-and-run-workspace.md | 354 ++++++++++++------
.../2026-04-bootstrap-migrations-cleanup.md | 23 +-
c-work/_CHANGELOG.md | 6 +
3 files changed, 257 insertions(+), 126 deletions(-)
diff --git a/c-work/1-plan/2026-04-trustee-workflow-audit-and-run-workspace.md b/c-work/1-plan/2026-04-trustee-workflow-audit-and-run-workspace.md
index 8fbeb62..c6f72ee 100644
--- a/c-work/1-plan/2026-04-trustee-workflow-audit-and-run-workspace.md
+++ b/c-work/1-plan/2026-04-trustee-workflow-audit-and-run-workspace.md
@@ -24,183 +24,301 @@ Zwei Themen:
**Geschaeftstreiber:** UX-Bruch ("ich war kurz auf einer anderen Seite, jetzt
ist mein Report weg") + fehlende Single-Source-of-Truth fuer Workflow-
-Outputs. Plus: User-Anforderung, dass jeder Workflow eine
-`FeatureInstance` referenziert (auch bei Scheduled-Runs), damit Datenquellen
-im Editor sauber gefiltert werden koennen.
+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 `targetFeatureInstanceId` reicht.
+
+### 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}/execute` ist heute **synchron** -- der
- Browser blockiert auf dem Request bis Ende. Der "Workspace" muss diesen
- Request nicht aendern, er muss nur die persistierten `AutoRun`-Daten
- verlinkbar/auffindbar machen.
-- `Workflow.featureInstanceId` ist heute optional (zumindest historisch).
- Pflicht-Binding ist breakend fuer existierende Workflows ohne Instanz --
- Migration noetig.
-- Scheduler-Pfad muss die Pflicht-Binding **respektieren**, sonst laufen
- Cron-Workflows ohne Daten-Scope.
-- Tab-Position: User wuenscht den Workspace explizit unter
- `/automations` (Seite "Nutzung > Automation") als zusaetzlichen Tab neben
- Dashboard + Workflows.
+- `POST /api/workflows/{instanceId}/execute` ist heute **synchron** (`await
+ executeGraph(...)`, kein BackgroundTask). Der Workspace muss den Request
+ nicht aendern -- er macht nur die persistierten `AutoRun`-Daten
+ auffindbar.
+- `AutoWorkflow.featureInstanceId` = GE-Instanz (Eigentuemer). NICHT
+ anfassen. Neues Feld `targetFeatureInstanceId` daneben.
+ Hinweis: Pydantic-Model hat `featureInstanceId: str` (required, nicht
+ Optional), aber DB-Spalte ist `nullable=YES`. System-Templates setzen
+ `featureInstanceId=""` oder ueberspringen die Validation.
+- System-Templates (`isTemplate=True`) haben `targetFeatureInstanceId=NULL`
+ (Template hat kein konkretes Ziel, erst die Kopie).
+- Scheduler bindet `targetFeatureInstanceId` vom persistierten Workflow --
+ kein Override moeglich.
+- Tab-Position: User wuenscht Workspace explizit unter `/automations`
+ (Seite "Nutzung > Automation") als zusaetzlichen Tab neben Dashboard +
+ Workflows.
+- `AutoRun` hat heute KEIN `featureInstanceId`-Feld -- Instanz-Zuordnung
+ laeuft indirekt ueber `AutoRun.workflowId -> AutoWorkflow.targetFeatureInstanceId`.
## Ziel und Nicht-Ziele
- Ziel A1: Audit-Befund GREEN dokumentiert (Wiki-Update).
-- Ziel A2: Generische Workflow-Run-Workspace-View; jeder Workflow hat
- Pflicht-FeatureInstance-Binding; Trustee-Views zeigen Resultate ueber
- Workspace statt eigenem React-State.
-- NICHT: Async-Umstellung des Execute-Endpoints (eigener Plan, falls noetig).
-- NICHT: Browser-Push-Notification (User wollte nur Workspace).
-- NICHT: Aenderung der Workflow-Engine-Logik selbst.
+- 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:
- `gateway/modules/features/graphicalEditor/datamodelFeatureGraphicalEditor.py`
- (`Workflow.featureInstanceId` Pflicht).
+ (neues Feld `AutoWorkflow.targetFeatureInstanceId`).
- `gateway/modules/features/graphicalEditor/routeFeatureGraphicalEditor.py`
- (Save-Validation, neue Aggregat-Routen unter `/api/automations/runs/...`).
- - `gateway/modules/features/graphicalEditor/mainScheduler.py` (Schedule-
- Erstellung uebernimmt Instanz aus Workflow).
- - `gateway/modules/features/trustee/mainTrustee.py` (Audit-Notiz,
- sicherstellen dass Templates `featureInstanceId` setzen).
+ (Save-Validation: non-template braucht `targetFeatureInstanceId`).
+ - `gateway/modules/features/graphicalEditor/interfaceFeatureGraphicalEditor.py`
+ (createWorkflow/updateWorkflow: Feld durchreichen).
+ - `gateway/modules/workflows/automation2/executionEngine.py`
+ (`executeGraph`: `{{featureInstanceId}}` aus `targetFeatureInstanceId`
+ resolven).
+ - `gateway/modules/workflows/scheduler/mainScheduler.py` (Schedule-Fire
+ nutzt `targetFeatureInstanceId` vom Workflow).
+ - `gateway/modules/features/trustee/mainTrustee.py` (Templates pruefen:
+ `targetFeatureInstanceId` wird bei `_copyTemplateWorkflows` gesetzt).
+ - `gateway/modules/interfaces/interfaceFeatures.py`
+ (`_copyTemplateWorkflows`: `targetFeatureInstanceId` auf Ziel-Instanz
+ setzen bei Kopie).
+ - Neue Route-Datei: `gateway/modules/routes/routeAutomationWorkspace.py`
+ (User-facing `/api/automations/runs/...` Endpoints).
- Frontend:
- Neuer Tab in `frontend_nyla/src/pages/AutomationsDashboardPage.tsx`.
- Neue Komponenten `WorkflowRunWorkspaceView`,
`WorkflowRunDetailView`.
- - FlowEditor-Toolbar bekommt Pflicht-Selector "Feature-Instanz".
- - `frontend_nyla/src/pages/views/trustee/TrusteeAnalyseView.tsx` und
- `TrusteeAbschlussView.tsx` schlanker (Verlinkung in den Workspace
- statt eigene Result-Anzeige; Run-State nicht mehr verloren bei Tab-
- Wechsel weil Persistenz uebernimmt).
-- DB-Migration: ja -- Bestand-Workflows ohne `featureInstanceId` brauchen
- ein Migrations-Skript (interaktiv).
-- RBAC: Zugriff auf `/automations/workspace` -- prueft pro Run die
- Mandate/FeatureInstance-Rechte des Users.
+ - FlowEditor CanvasHeader: Pflicht-Dropdown "Ziel-Instanz"
+ (`targetFeatureInstanceId`).
+ - `TrusteeAnalyseView.tsx`: Inline-Result-Anzeige raus, Link zum
+ Workspace-Detail.
+ - `TrusteeAbschlussView.tsx`: analog -- Link zum Workspace-Detail.
+- DB-Migration: `AutoWorkflow` bekommt neue Spalte
+ `targetFeatureInstanceId` (nullable, wegen Templates). Bestand:
+ Dev-Audit zeigt 0 non-template Workflows ohne Instance (alle 24
+ regulaeren Workflows haben bereits `featureInstanceId` aus dem URL-
+ Context). Migration: fuer Bestand `targetFeatureInstanceId :=
+ featureInstanceId` setzen (selbe Instanz wie GE = Default fuer alte
+ generische Workflows ODER aus Graph-Nodes extrahieren wenn dort
+ konkreter Wert steht).
+- RBAC: Neue Endpoints pruefen `targetFeatureInstanceId` gegen User-
+ Berechtigungen via FeatureAccess.
## Befund A1 (Audit) -- bereits GREEN
-| Service | Workflow-ID | Backend-Definition | Status | Beleg |
-|---------|-------------|---------------------|--------|-------|
-| Budget-Vergleich | `trustee-budget-comparison` | `mainTrustee.py` 430-461 | GREEN | DataRef Trigger-Payload + Refresh-Output, modern |
-| KPI-Dashboard | `trustee-kpi-dashboard` | `mainTrustee.py` 463-478 | GREEN | `_buildAnalysisWorkflowGraph` 364-381 |
-| Cashflow-Rechnung | `trustee-cashflow` | `mainTrustee.py` 480-492 | GREEN | dito |
-| Prognose | `trustee-forecast` | `mainTrustee.py` 494-507 | GREEN | dito |
-| Jahresabschluss-Pruefung | `trustee-year-end-check` | `mainTrustee.py` 509-522 | GREEN | dito |
+| 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` ruft `materializeFeatureInstanceRefs` +
-`validateGraph` vor jedem Lauf auf
-(`gateway/modules/features/graphicalEditor/executionEngine.py` 341-350).
-Persistierte Graphs nutzen zunaechst nackte UUID fuer `featureInstanceId`
-(Bootstrap `interfaceFeatures.py` 336-338), Laufzeit-Envelope erfolgt in
-`executeGraph`.
+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 | Workspace ist GENERISCH plattformweit, nicht Trustee-spezifisch | Doppelt-Bauen vermeiden; ComCoach/TeamsBot/Workspace profitieren auch |
+| 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 | Workflow-Speicherung verlangt `featureInstanceId` | Datenquellen-Tools im Editor sind nur dann sinnvoll filterbar |
-| 2026-04-29 | Bestehende Workflows ohne Instanz: interaktives Migrations-Skript pro Mandate | Kein automatischer Default -- der User soll bewusst zuordnen |
-| 2026-04-29 | Browser-Push-Notification NICHT umgesetzt | User hat nur Workspace gewaehlt; Toast bei Run-Ende reicht |
+| 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 -- FeatureInstance-Pflicht-Binding
+### Phase 1 -- targetFeatureInstanceId + Validation
-- [ ] Backend: `Workflow.featureInstanceId NOT NULL` (Pydantic +
- DB-Constraint via Connector-Auto-Init).
-- [ ] Save-Validation in `routeFeatureGraphicalEditor.py`: Workflow ohne
- `featureInstanceId` -> 400 mit Error-Detail "Feature-Instanz fehlt".
- Save-with-errors (AC-9 vom Typed-Action-Plan) bleibt erlaubt -- nur
- Run-Start blockt.
-- [ ] Run-Start (`POST .../execute`): Vorab-Check, dass Workflow eine
- Instanz hat.
-- [ ] Migrations-Skript
- `gateway/scripts/script_db_migrate_workflow_feature_instance.py`:
- listet pro Mandate alle Workflows ohne Instanz, bietet pro Workflow
- eine Auswahl (CLI prompt).
-- [ ] Scheduler: Schedule-Erstellung uebernimmt Instanz aus Workflow,
- Override nicht moeglich.
-- [ ] FlowEditor-Toolbar: Pflicht-Dropdown "Feature-Instanz" oben links.
- Datenquellen-Tools (z.B. UDB-Listen, FileItems, SourcesTab) filtern
- automatisch auf diese Instanz.
-- [ ] Trustee-Templates pruefen: alle Workflows haben in
- `mainTrustee.py` bereits `featureInstanceId` als
- `{{featureInstanceId}}`-Placeholder, der beim
- `_copyTemplateWorkflows` ersetzt wird (`interfaceFeatures.py`
- 269-338) -- nur sicherstellen dass jetzt nichts haengen bleibt.
+- [ ] `datamodelFeatureGraphicalEditor.py`: `targetFeatureInstanceId: Optional[str] = Field(default=None, ...)`
+ (nullable wegen Templates).
+- [ ] `interfaceFeatureGraphicalEditor.py` `createWorkflow`: aus Body oder
+ Fallback `self.featureInstanceId` uebernehmen.
+- [ ] `interfaceFeatureGraphicalEditor.py` `updateWorkflow`: Feld im
+ Update-Payload erlauben (NICHT strippen wie `featureInstanceId`).
+- [ ] `routeFeatureGraphicalEditor.py` Save-Endpoint: wenn
+ `isTemplate=False` und `targetFeatureInstanceId` fehlt/leer -> 400.
+- [ ] `routeFeatureGraphicalEditor.py` Execute-Endpoint: Vorab-Check
+ `targetFeatureInstanceId` vorhanden, User hat FeatureAccess darauf.
+- [ ] `executionEngine.py` `executeGraph`: **NEU BAUEN** -- VOR
+ `materializeFeatureInstanceRefs` eine Placeholder-Substitution
+ einfuegen: alle `{{featureInstanceId}}`-Vorkommen im serialisierten
+ Graph-JSON durch `targetFeatureInstanceId` (uebergeben als neuer
+ Parameter) ersetzen. Bestehende hart-kodierte UUIDs in Nodes bleiben
+ unangetastet (Backward-Compat fuer kopierte Templates, wo
+ `_copyTemplateWorkflows` bereits pre-baked hat).
+ Hinweis: `materializeFeatureInstanceRefs` macht typed-ref-envelope-
+ Rewriting, NICHT Placeholder-Substitution -- beides ist noetig.
+- [ ] `mainScheduler.py`: Schedule-Fire liest heute
+ `workflow["featureInstanceId"]` (= GE-Instanz) und uebergibt es als
+ `instanceId` an `executeGraph`. Aendern: zusaetzlich
+ `workflow["targetFeatureInstanceId"]` lesen und als neuen Parameter
+ `targetFeatureInstanceId` an `executeGraph` uebergeben (fuer die
+ Placeholder-Substitution). `instanceId` (GE-Instanz) bleibt fuer
+ RBAC-Scope bestehen.
+- [ ] `interfaceFeatures.py` `_copyTemplateWorkflows`: bei Kopie
+ `targetFeatureInstanceId = instanceId` (die Ziel-Feature-Instanz)
+ EXPLIZIT im `createWorkflow`-Payload setzen. Hinweis: heute wird
+ `featureInstanceId` NICHT im Payload gesetzt, sondern kommt implizit
+ aus dem GE-Interface-Context (`getGraphicalEditorInterface(...,
+ instanceId)`). `targetFeatureInstanceId` muss 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 `targetFeatureInstanceId` das User nicht
+ zugreifen darf -> 403.
-### Phase 2 -- Generischer WorkflowRunWorkspace
+### Phase 2 -- FlowEditor Toolbar Dropdown
-- [ ] Neue Aggregat-API:
- `GET /api/automations/runs?scope=mine|mandate|all&status=...&limit=...`
- und `GET /api/automations/runs/{runId}/detail`. Detail-Payload
- kombiniert AutoRun + AutoStepLog + Outputs + verlinkte FileItems
- (joined mit RBAC-Filter).
-- [ ] Tab "Workspace" in
- `frontend_nyla/src/pages/AutomationsDashboardPage.tsx` (neben
- Dashboard, Workflows).
-- [ ] Komponente `WorkflowRunWorkspaceView`: Liste mit Filter
- (Status, Workflow-Template, Mandate, Zeitraum), 50-er Pagination.
+- [ ] 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 `targetFeatureInstanceId` leer (z.B. frisch aus Template):
+ User MUSS erst Instanz waehlen bevor Save/Run moeglich.
+
+### Phase 3 -- Generischer WorkflowRunWorkspace
+
+- [ ] Neue Route-Datei `gateway/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 auf
+ `targetFeatureInstanceId` hat.
+ - `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, FeatureInstance.
+ - 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 (`a href="/api/files/{id}/download"`).
-- [ ] Trustee-Views umbauen:
- - `TrusteeAnalyseView`: Result-Anzeige raus (`resultText` /
- `resultDocuments` State), stattdessen "Im Workspace ansehen"-
- Button mit `runId`.
- - `TrusteeAbschlussView`: dasselbe (heute zeigt der gar nichts).
+ Direkt-Download.
-### Phase 3 -- Notifications
+### Phase 4 -- Trustee-Views Refactor + Notifications
-- [ ] Toast bei Run-Ende ist heute schon da -- erweitern um Klick-Action
- zum Detail-View des Runs.
-- [ ] Sidebar-Badge auf Eintrag "Automation" (Counter "neu seit letztem
- Besuch", `localStorage`-basiert).
+- [ ] `TrusteeAnalyseView`: `resultText` / `resultDocuments` State
+ (Z. ~124-125) und die zugehoerige Output-Extraktion (Z. ~202-253)
+ raus. Nach Run-Ende: Navigation/Link zum Workspace-Detail (`runId`).
+- [ ] `TrusteeAbschlussView`: hat KEIN `resultText`/`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 Workflow ohne Instanz, When User Save klickt, Then 400 mit klarer Error-Message | must |
-| 2 | Given Bestand-Workflow ohne Instanz, When Migrations-Skript laeuft, Then User waehlt interaktiv pro Workflow eine Instanz und das Feld wird gesetzt | must |
-| 3 | Given Trustee-Run gestartet, When User waehrend Lauf die Seite wechselt und zurueck zum Tab "Workspace" geht, Then Run mit allen Outputs sichtbar | must |
+| 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 Run-Ende, When Toast erscheint, Then Klick fuehrt direkt zum WorkflowRunDetailView | should |
-| 6 | Given neue Runs seit letztem Besuch, When User die Sidebar sieht, Then "Automation" hat einen Counter-Badge | should |
-| 7 | Given FlowEditor offen ohne Instanz-Auswahl, When User Datenquellen-Tool oeffnen will, Then Hinweis "Bitte Feature-Instanz waehlen" | should |
+| 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 | gateway/tests/features/graphicalEditor/test_workflow_save_requires_instance.py | pending |
-| T2 | 2 | manual | nein | gateway/scripts/script_db_migrate_workflow_feature_instance.py | pending |
-| T3 | 3 | e2e | ja | frontend_nyla/tests/e2e/workflow-run-workspace.spec.ts | pending |
-| T4 | 4 | e2e | ja | wie T3 | pending |
-| T5 | 5 | manual | nein | -- | pending |
-| T6 | 6 | unit | ja | frontend_nyla/src/pages/__tests__/AutomationsDashboardPage.test.tsx | pending |
-| T7 | 7 | unit | ja | frontend_nyla/src/components/flowEditor/__tests__/Toolbar.test.tsx | pending |
+| T1 | 1 | api | ja | gateway/tests/features/graphicalEditor/test_workflow_save_target_instance.py | pending |
+| T2 | 2 | api | ja | gateway/tests/features/graphicalEditor/test_workflow_execute_rbac.py | pending |
+| T3 | 3,4 | e2e | ja | frontend_nyla/tests/e2e/workflow-run-workspace.spec.ts | pending |
+| T4 | 5 | unit | ja | frontend_nyla/src/components/flowEditor/__tests__/TargetInstanceDropdown.test.tsx | pending |
+| T5 | 8 | api | ja | gateway/tests/features/test_copy_template_workflows.py | pending |
+
+## Dev-DB-Befund (2026-04-29)
+
+- DB: `poweron_graphicaleditor`, Tabelle `AutoWorkflow`
+- 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 := featureInstanceId` fuer
+ 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 2026-04-29.
+- Audit-Quelle: Subagent-Report + DB-Query 2026-04-29.
+- Code-Cross-Check: Subagent 2026-04-29 (16 Annahmen verifiziert).
- Wiki: `wiki/b-reference/gateway/workflow.md`,
- `wiki/b-reference/gateway/features/trustee.md`,
- `wiki/c-work/4-done/2026-04-typed-action-architecture.md`,
- `wiki/c-work/4-done/2026-04-automation-central-admin.md`.
+ `wiki/b-reference/gateway/features/trustee.md`.
## Abschluss
- [ ] `wiki/b-reference/gateway/workflow.md` Abschnitt
- "Workflow-Run-Workspace" anlegen
+ "targetFeatureInstanceId + Workspace" anlegen
- [ ] `wiki/b-reference/gateway/features/trustee.md` Result-UX-Sektion
aktualisieren
- [ ] `wiki/TOPICS.md` ggf. Tab-Beschreibung
-- [ ] Dieses Dokument -> `z-archive/` verschoben
+- [ ] Dieses Dokument -> `4-done/` verschoben
diff --git a/c-work/4-done/2026-04-bootstrap-migrations-cleanup.md b/c-work/4-done/2026-04-bootstrap-migrations-cleanup.md
index 6647b06..8a32499 100644
--- a/c-work/4-done/2026-04-bootstrap-migrations-cleanup.md
+++ b/c-work/4-done/2026-04-bootstrap-migrations-cleanup.md
@@ -98,16 +98,23 @@ Falls trotzdem Restbestand auftauchen sollte (alter DB-Restore o.ae.), feuert
nach dem Boot der Telemetrie-Helper eine WARN-Logzeile pro betroffenem Check
mit Routine-Name + IDs. Dann manuell mit dem Audit-Skript untersuchen.
-## Folgearbeiten (separate Plans)
+## Folgearbeiten
-- **Scripts-Inventar:** subagent hat 27 Files in `gateway/scripts/` kategorisiert.
- Klar Archiv-Kandidaten (B): `check_orphan_featureinstance.py` (hardcoded UUIDs),
- `script_db_cleanup_duplicate_roles.py` (IS-NULL-Bug-Fix; Bug laengst gefixt).
- Unklar (C): `_listMandates.py`, `migrate_async_to_sync.py`,
- `i18n_rekey_plaintext_keys.py`, `script_db_migrate_accessrules_objectkeys.py`.
- Restliche 21 Files = AKTIV. Entscheidung pro File offen.
+- **Scripts-Cleanup -- erledigt 2026-04-29:** 5 one-shot-Scripts in
+ `gateway/scripts/_archive/` mit README, `_listMandates.py` geloescht.
+ Aktiv-Bestand: 21 Scripts inkl. dem neuen `script_db_audit_legacy_state.py`.
- **Telemetrie-Lebenszyklus:** in 30+ Tagen pruefen ob Telemetrie-Helper
- Treffer hatte. Wenn 0, Helper komplett entfernen.
+ Treffer hatte. Wenn 0, `_legacyMigrationTelemetry.py` komplett entfernen.
+
+## Script-Archiv-Inhalt (2026-04-29)
+
+| Datei | Migrationsthema | Begruendung |
+|-------|-----------------|-------------|
+| `check_orphan_featureinstance.py` | Vor-Ort-Check mit hardcoded UUIDs | Ad-hoc fuer einen konkreten Vorfall |
+| `script_db_cleanup_duplicate_roles.py` | Cleanup doppelter Roles wegen IS-NULL-Bug | Bug laengst gefixt, Cleanup durchgelaufen |
+| `migrate_async_to_sync.py` | One-shot Codemod `async def` -> `def` | Refactor abgeschlossen |
+| `i18n_rekey_plaintext_keys.py` | Frontend Dot-Notation -> Klartext-Keys | Migration durch (`4-done/2026-04-ui-i18n-dynamic-language-sets.md`) |
+| `script_db_migrate_accessrules_objectkeys.py` | AccessRule-Items kurz -> qualifiziert | Navigation-API live |
## Akzeptanzkriterien
diff --git a/c-work/_CHANGELOG.md b/c-work/_CHANGELOG.md
index fc967f7..dd8982e 100644
--- a/c-work/_CHANGELOG.md
+++ b/c-work/_CHANGELOG.md
@@ -14,6 +14,12 @@ Skip: reine Refactors, Formatting, Lint, Dep-Bumps, Test-only, Wiki-Tippfehler.
## 2026-04-29
+- 2026-04-29 | feat | gateway, frontend-nyla | **A2 Workflow-Run-Workspace + targetFeatureInstanceId implementiert.** Phase 1: `AutoWorkflow.targetFeatureInstanceId` (Pydantic + DB), createWorkflow-Fallback, Save-Validation (400 fuer non-template ohne targetId), Execute-RBAC-Check, `executeGraph` Placeholder-Substitution (`{{featureInstanceId}}`), Scheduler-Durchreichung, `_copyTemplateWorkflows` explizites Setzen, idempotente Boot-Backfill-Migration. Phase 2: FlowEditor CanvasHeader "Ziel-Instanz" Dropdown mit FeatureStore-Integration, Save/Load mit `targetFeatureInstanceId`. Phase 3: Neuer Backend-Endpoint `GET /api/automations/runs` + `GET /api/automations/runs/{runId}/detail` (RBAC via FeatureAccess), neuer "Workspace" Tab in AutomationsDashboardPage mit Run-Liste + Detail-View (Steps, Files, Download). Phase 4: TrusteeAnalyseView inline-Results durch "Im Workspace ansehen"-Link ersetzt, TrusteeAbschlussView Workspace-Link nach Completion ergaenzt. (c-work: `1-plan/2026-04-trustee-workflow-audit-and-run-workspace.md`)
+
+- 2026-04-29 | docs | wiki | **Plan A2 finalisiert: Trustee Workflow-Audit + Generischer Workflow-Run-Workspace.** Cross-Check gegen Codebase (12 Punkte verifiziert, 4 Korrekturen eingearbeitet): `executeGraph` hat keine Placeholder-Substitution (muss neu gebaut werden), `_copyTemplateWorkflows` setzt `featureInstanceId` nicht im Payload (implizit via GE-Interface), `TrusteeAbschlussView` hat kein `resultText`/`resultDocuments` (nur status/summary), 7 Trustee-Templates statt 5 im Audit. Risiko-Sektion ergaenzt. (c-work: `1-plan/2026-04-trustee-workflow-audit-and-run-workspace.md`)
+
+- 2026-04-29 | chore | gateway | **`gateway/scripts/` aufgeraeumt: 5 obsolete one-shot-Scripts archiviert + 1 Ad-hoc-Snippet geloescht.** Im Anschluss an den Bootstrap-Cleanup. Neuer Unter-Ordner `gateway/scripts/_archive/` mit eigenem README beschreibt Inhalt + Begruendung pro Datei. Verschoben (mit User-Bestaetigung pro File): `check_orphan_featureinstance.py` (hardcoded Vor-Ort-UUIDs), `script_db_cleanup_duplicate_roles.py` (`IS NULL`-Bug-Cleanup, Bug laengst gefixt), `migrate_async_to_sync.py` (one-shot `async def` -> `def` Codemod, Refactor durch), `i18n_rekey_plaintext_keys.py` (Frontend Klartext-Keys, Migration durch siehe `4-done/2026-04-ui-i18n-dynamic-language-sets.md`), `script_db_migrate_accessrules_objectkeys.py` (Navigation-API-Migration MIGRATION_MAP nur fuer trustee+realestate hardcoded, durch). Geloescht: `_listMandates.py` (26-Zeilen Ad-hoc-Debug-Snippet, jederzeit aus Git rekonstruierbar). Status danach: 21 aktive Scripts in `gateway/scripts/` (inkl. neues `script_db_audit_legacy_state.py`) + 5 archivierte Scripts. Dev-Boot 20:24:09 bestaetigt das aufgeraeumte interfaceBootstrap.py + Telemetrie-Hook laufen sauber durch (kein WARN, kein Restbestand, neue Code-Pfad-Line-Refs). (c-work: `4-done/2026-04-bootstrap-migrations-cleanup.md`)
+
- 2026-04-29 | refactor | gateway | **Bootstrap-Cleanup ausgefuehrt: 4 idempotente Migrations-Routinen + 1 Aggregations-Fallback aus dem Boot-Pfad entfernt.** Vor Removal Audit-Skript `gateway/scripts/script_db_audit_legacy_state.py` (NEU, lese-only, exit-code-gated) gegen Dev-DB gelaufen -> 4/5 GREEN sofort, Check 5 (RAG-Orphans) RED mit 20 verwaisten `FileContentIndex`-Rows ohne `mandateId`/`featureInstanceId` -> via `--purge-rag-orphans` bereinigt -> Re-Audit 5/5 GREEN. Dann entfernt aus `gateway/modules/interfaces/interfaceBootstrap.py`: `_migrateMandateDescriptionToLabel` (Funktion + Aufruf), `_migrateMandateNameLabelSlugRules` (Funktion + Aufruf, ~64 Zeilen), `initRootMandate`-Legacy-Block (`name="Root"`-Migration, 7 Zeilen; Funktion selbst bleibt), `_migrateAndDropSysAdminRole` (Funktion + Aufruf, ~95 Zeilen). In `interfaceDbKnowledge.py`: `aggregateMandateRagTotalBytes`-Fallback-Block (`try`/`except` mit FileItem-ID-Korrelation aus Management-DB, ~27 Zeilen) entfernt -- die Funktion bleibt aktiv, da sie 4 externe Caller hat. Ersatz: neuer Helper `gateway/modules/interfaces/_legacyMigrationTelemetry.py` mit 4 lese-only WARN-Checks (gleiche Logik wie Audit-Skript, prozessweit gecached) wird am Ende von `initBootstrap` einmalig aufgerufen -- falls je doch Restbestand auftauchen sollte (alter DB-Restore o.ae.), gibt's klare WARN-Logs mit Routine-Name + IDs. Tests `tests/unit/bootstrap/test_mandateNameMigration.py` (8 Tests) und `tests/unit/rbac/test_sysadmin_migration.py` (5 Tests) geloescht (referenzierten entfernte Funktionen, wuerden ImportError werfen). Smoke-Test: 17/17 verbliebene rbac+bootstrap-Tests GREEN, Imports aller drei Module GREEN. User-Statement zur Risk-Lage: "die codebase lief bereits auf int und main/prod" -- d.h. die idempotenten Migrations sind dort schon mehrfach durchgelaufen, das Removal-Risiko = 0; das Audit-Skript bleibt fuer pre-deploy-Gating. Plan urspruenglich in `1-plan/`, jetzt direkt in `4-done/2026-04-bootstrap-migrations-cleanup.md`. (c-work: `4-done/2026-04-bootstrap-migrations-cleanup.md`)
- 2026-04-29 | feat | frontend-nyla, gateway | **Generischer `frontendType: templateTextarea`** fuer Freitext mit `{{nodeId.path}}`-Variablen (DataPicker-Insert); `email.draftEmail.context` und `ai.prompt.aiPrompt` nutzen ihn statt reiner Textarea -- Aufloesung ausschliesslich via bestehendes `resolveParameterReferences` (kein Loop-Spezialcode im Executor). Loop-Preview-Enrichment + IfElse/AI-`responseData`-Picker bleiben. Unit-Test `test_legacy_string_template_loop_current_item_nested` in `test_automation2_graphUtils.py`.