solution design customer cases

This commit is contained in:
ValueOn AG 2026-06-06 00:33:47 +02:00
parent ca1101a7f0
commit 8ed1d81b7a
7 changed files with 760 additions and 13 deletions

View file

@ -1,6 +1,6 @@
<!-- status: canonical -->
<!-- lastReviewed: 2026-05-17 -->
<!-- verifiedAgainst: gateway (codebase audit 2026-04-07, post Automation Unification) + Typed Action Architecture Phasen 1-5 + Infomaniak-Connector (2026-04-28) + MSFT/Google Calendar+Contacts + Reconnect (2026-04-28) + Google Voice STT/TTS (2026-05-10) + BackgroundJob i18n payload (2026-05-17) -->
<!-- lastReviewed: 2026-06-05 -->
<!-- verifiedAgainst: gateway (codebase audit 2026-04-07, post Automation Unification) + Typed Action Architecture Phasen 1-5 + Infomaniak-Connector (2026-04-28) + MSFT/Google Calendar+Contacts + Reconnect (2026-04-28) + Google Voice STT/TTS (2026-05-10) + BackgroundJob i18n payload (2026-05-17) + Feature Lifecycle Hooks (2026-06-05) -->
# Gateway -- Architektur
@ -20,7 +20,7 @@ Unter `platform-core/modules/` (Kontext-Audit):
| `datamodels/` | Pydantic-Datenmodelle (u. a. Ai, Billing, Chat, Content, Files, Knowledge, Rbac, Subscription, UiLanguage, Workflow) |
| `features/` | Feature-Module (autonome Domänen): workspace, graphicalEditor, commcoach, neutralization, realEstate, trustee, teamsbot |
| `interfaces/` | DB-Interfaces (App, Billing, Chat, Knowledge, Management, Subscription), AI-Objects, RBAC, Features, Messaging |
| `migration/` | Daten-Migrationen |
| `dbHelpers/` | DB-Hilfsfunktionen (Registry, Audit-Logger, FK-Resolver, Pagination, Multi-Tenant-Optimierungen) |
| `routes/` | REST-API-Routen (u. a. Admin, Billing, DataFiles, DataSources, i18n, Security, Store, System) |
| `security/` | RBAC (`rbac.py`, `rbacCatalog.py`), Root-Access |
| `serviceCenter/` | Zentrale Service-Orchestrierung (Registry, Resolver, Kontext, Haupt-Services) |
@ -78,6 +78,103 @@ Die genannten Module kapseln die Datenbankzugriffe bzw. die zugehörigen Fachver
Weitere Interface-Dateien im Ordner (z. B. Voice, Tickets, Messaging, Bootstrap) erfüllen dieselbe Normalisierungsrolle gegenüber Connectors bzw. externen APIs; die Tabelle oben entspricht der expliziten DB-/Kern-Zuordnung aus dem Kontext-Audit.
## Feature Lifecycle Hooks
Features sind autonome Module unter `modules/features/<featureCode>/`. Jedes Feature hat ein `main<Feature>.py` mit Registrierungs- und Lifecycle-Funktionen. Die Core-Schicht ruft diese **dynamisch** via `system/registry.py``loadFeatureMainModules()` auf — es gibt **keine hardcodierten Feature-Imports** in den Interfaces.
### Pflicht-Funktionen (Feature-Registrierung)
| Funktion | Beschreibung |
|----------|-------------|
| `getFeatureDefinition() -> Dict` | Feature-Metadaten: `code`, `label`, `icon`, `autoCreateInstance` |
| `registerFeature(catalogService) -> bool` | RBAC-Objekte (UI, Resource, Data) im Katalog registrieren |
| `getUiObjects() -> List[Dict]` | UI-Objekte fuer RBAC-Sichtbarkeit |
| `getResourceObjects() -> List[Dict]` | Resource-Objekte fuer RBAC |
| `getTemplateRoles() -> List[Dict]` | Template-Rollen mit AccessRules |
### Optionale Lifecycle Hooks
Jedes Feature kann diese Funktionen in seinem `main<Feature>.py` definieren. Die Core-Schicht ruft sie automatisch auf, wenn die entsprechende Situation eintritt:
| Hook | Signatur | Wann aufgerufen | Wo aufgerufen |
|------|----------|-----------------|---------------|
| `onMandateDelete` | `(mandateId: str, instances: list) -> None` | Mandate wird geloescht (hard-delete) | `interfaceDbApp.py` (Cascade-Delete-Loop) |
| `onBootstrap` | `() -> None` | App-Start (nach DB-Initialisierung) | `interfaceBootstrap.py` (Boot-Sequenz) |
| `onInstanceCreate` | `(mandateId: str, instanceId: str, featureCode: str, templateWorkflows: list) -> int` | Feature-Instanz wird erstellt und das Feature hat Template-Workflows | `interfaceFeatures.py` (via graphicalEditor) |
| `onStart` | `async (eventUser) -> None` | App-Start (Feature-spezifische Hintergrund-Prozesse) | `app.py` (Startup-Event) |
| `onStop` | `async (eventUser) -> None` | App-Shutdown | `app.py` (Shutdown-Event) |
| `getTemplateWorkflows` | `() -> List[Dict]` | Bootstrap / Instance-Create (Workflow-Templates bereitstellen) | `mainGraphicalEditor.onBootstrap()` / `onInstanceCreate()` |
### Aufruf-Pattern (Core-Seite)
```python
from modules.system.registry import loadFeatureMainModules
for featureCode, mod in loadFeatureMainModules().items():
hook = getattr(mod, "onMandateDelete", None)
if hook:
try:
hook(mandateId, instances)
except Exception as e:
logger.warning(f"onMandateDelete hook for '{featureCode}' failed: {e}")
```
### Implementierungs-Status
Alle Features haben `onMandateDelete` implementiert:
| Feature | DB | `onMandateDelete` | `onBootstrap` | `onInstanceCreate` | Besonderheiten |
|---------|----|----|----|----|-----|
| graphicalEditor | poweron_graphicaleditor | Yes | Yes | Yes | Vollstaendige Hooks |
| trustee | poweron_trustee | Yes | — | — | 13 Models |
| commcoach | poweron_commcoach | Yes | — | — | Nutzt `instanceId` statt `featureInstanceId` |
| teamsbot | poweron_teamsbot | Yes | — | — | Nutzt `instanceId`; Child-Records via Session-FK |
| neutralization | poweron_neutralization | Yes | — | — | 2 Models |
| workspace | poweron_workspace | Yes | — | — | 1 Model |
| realEstate | poweron_realestate | Yes | — | — | Fallback auf `mandateId` |
| redmine | poweron_redmine | Yes | — | — | 3 Models inkl. Ticket-Mirrors |
### Beispiel: Neues Feature mit Lifecycle
```python
# modules/features/myFeature/mainMyFeature.py
FEATURE_CODE = "myFeature"
def getFeatureDefinition() -> Dict[str, Any]:
return {"code": FEATURE_CODE, "label": "My Feature", "icon": "mdi-star", "autoCreateInstance": False}
def onMandateDelete(mandateId: str, instances: list) -> None:
"""Eigene Daten aufraumen wenn ein Mandate geloescht wird."""
# Hier: Verbindung zur eigenen DB, Records loeschen
pass
def onBootstrap() -> None:
"""Eigene System-Daten seeden beim App-Start."""
pass
```
### Architektur-Prinzipien
1. **Kein Aufwaerts-Import:** Die Core-Schicht (interfaces) importiert NIEMALS direkt aus `features/`. Alle Feature-spezifische Logik wird ueber Hooks und die Registry aufgerufen.
2. **Feature raeumt selbst auf:** Jedes Feature ist verantwortlich fuer seine eigenen Daten (eigene DB, eigene Models). Die Core-Schicht delegiert Lifecycle-Events.
3. **Fail-safe:** Hook-Fehler werden geloggt, brechen aber nicht den Boot oder die Mandate-Deletion ab.
4. **Dynamisch erweiterbar:** Neue Features muessen nur die Hook-Funktion definieren — kein Code in der Core-Schicht muss angepasst werden.
### Workflow-Automation Models
Die Workflow-Engine-Models leben kanonisch in `datamodels/datamodelWorkflowAutomation.py`:
| Model | Beschreibung | DB |
|-------|-------------|-----|
| `AutoWorkflow` | Workflow-Definition (Graph, Triggers, Templates) | poweron_graphicaleditor |
| `AutoVersion` | Versionierter Graph-Snapshot | poweron_graphicaleditor |
| `AutoRun` | Laufende/abgeschlossene Ausfuehrung | poweron_graphicaleditor |
| `AutoStepLog` | Protokoll pro Knoten-Ausfuehrung | poweron_graphicaleditor |
| `AutoTask` | Human Tasks (Formulare, Approvals) | poweron_graphicaleditor |
Die DB-Konstante `GRAPHICAL_EDITOR_DATABASE = "poweron_graphicaleditor"` ist ebenfalls dort definiert. Feature-interne Re-Exports in `features/graphicalEditor/datamodelFeatureGraphicalEditor.py` existieren fuer Backward-Kompatibilitaet.
## Schlüssel-Dateien
| Datei / Pfad | Rolle |
@ -238,11 +335,32 @@ Konsequenzen:
- **Save-with-errors** ist explizit erlaubt (AC-9-Patch im `CanvasHeader`); Run wird mit `executeBlockedReason` blockiert, der Save-Toast meldet Pflicht-Fehler-Anzahl.
- **DB-Hygiene** ist optional via `script_migrate_feature_instance_refs.py` (`--dry-run` standard) -- ohne Skript laufen Workflows korrekt, der Editor zeigt aber bis zum naechsten Save noch das Legacy-Format.
## Layer-Hierarchie (strikte Importrichtung)
```
shared (L0) → 0 ausgehende Imports
datamodels (L1) → nur shared
connectors (L2) → shared, datamodels
dbHelpers (L3) → shared, datamodels, connectors
interfaces (L4) → L0-L3 + security, system
system (L4) → L0-L3
security (L4) → L0-L3
auth (L4) → L0-L4
serviceCenter (L5) → L0-L4
workflows (L5) → L0-L4 + features (Deferred: WorkflowAutomation)
features (L5) → L0-L4 + serviceCenter, workflows
routes (L6) → L0-L5
app (L7) → alle (Composition Root)
```
Jede Schicht importiert NUR von niedrigeren Schichten. Ausnahmen (z.B. workflows→features) sind dokumentiert und haben einen Refactoring-Plan.
## Regeln / Invarianten
- **Schichten:** Connectors sind anbieterspezifisch und ersetzbar; **Services hängen von Interfaces ab, nicht direkt von Connectors**. Geschäftslogik und Guardrails liegen in den Services.
- **Kein Aufwaerts-Import:** Interfaces importieren NICHT von features, routes oder serviceCenter. Feature-Interaktion laeuft ueber Lifecycle Hooks (siehe oben).
- **Datenbank:** Persistenz und die genannten Domänen-Reads/Writes laufen über die **Interface**-Schicht, nicht ad hoc aus Routen heraus.
- **Service Center:** Aufrufe laufen über die zentrale Auflösung (`getService` + Kontext); Workflows und Routen konsumieren dieselbe Landschaft.
- **Sicherheit & Governance:** RBAC und Geheimnisse werden zentral geführt; Service-Aufrufe und Workflow-Schritte sollen nachvollziehbar sein (Audit); Kontingente und Limits pro Mandant/Service sind vorgesehen.
- **HTTP-Einstieg:** Globale Middleware (CSRF, Token-Refresh unter `modules/auth/`); RBAC-Auswertung und Katalog unter `modules/security/`.
- **Code-Konventionen (Projekt):** Interne Hilfsfunktionen mit Präfix **`_`**; Bezeichner in **camelCase** für Variablen und Funktionen.
- **Code-Konventionen (Projekt):** Interne Hilfsfunktionen mit Präfix **`_`** (duerfen NICHT extern importiert werden); Bezeichner in **camelCase** für Variablen und Funktionen.

View file

@ -0,0 +1,178 @@
# CommCoach Communication Coach for Leaders
## Product Goal
An AI coaching agent for executives that:
- Captures topics, concerns, and questions
- Asks active diagnostic follow-up questions
- Builds a continuable context per topic (Dossier)
- Conducts daily training conversations
- Makes progress visible (Gamification)
- Supports voice natively (STT/TTS, voice selection)
## Architecture
### Layers
```
Transport (REST/SSE) → routeFeatureCommcoach.py
Orchestration → serviceCommcoach.py
AI Pipeline → serviceCommcoachAi.py
Scheduler → serviceCommcoachScheduler.py
Domain / Storage → interfaceFeatureCommcoach.py
Data Models → datamodelCommcoach.py
Feature Registration → mainCommcoach.py
```
### Reuse from Existing Codebase
| Component | Source | Usage |
|-----------|--------|-------|
| Feature Plug&Play | `registry.py` | Auto-discovery via `routeFeature*.py` |
| RequestContext + RBAC | `authentication.py`, `interfaceRbac.py` | Auth + ownership |
| DatabaseConnector | `connectorDbPostgre.py` | New DB `poweron_commcoach` |
| VoiceObjects (STT/TTS) | `interfaceVoiceObjects.py` | Voice pipeline |
| MessagingInterface | `interfaceMessaging.py` | Email summaries |
| SSE Pattern | workspace `routeFeatureWorkspace.py` | Chat streaming |
| PDF Renderer | `rendererPdf.py` | Dossier export (Iteration 2) |
| EventManagement | `eventManagement.py` | Scheduled reminders |
## Domain Model
### Entities
```
User (1) ──── owns ──── (N) CoachingContext
CoachingContext (1) ────── (N) CoachingSession
CoachingSession (1) ───── (N) CoachingMessage
CoachingContext (1) ────── (N) CoachingTask
CoachingContext (1) ────── (N) CoachingScore
User (1) ──────────────── (1) CoachingUserProfile
```
### Status Models
```
CoachingContext: active → paused → active | archived → active | completed
CoachingSession: active → completed | cancelled
CoachingTask: open → in_progress → done | skipped
```
## API Design
```
PREFIX: /api/commcoach/{instanceId}
# Contexts (Dossier)
GET /contexts
POST /contexts
GET /contexts/{contextId}
PUT /contexts/{contextId}
DELETE /contexts/{contextId}
POST /contexts/{contextId}/archive
POST /contexts/{contextId}/activate
# Sessions
GET /contexts/{contextId}/sessions
POST /contexts/{contextId}/sessions/start
GET /sessions/{sessionId}
POST /sessions/{sessionId}/complete
POST /sessions/{sessionId}/cancel
# Streaming Chat
POST /sessions/{sessionId}/message/stream
POST /sessions/{sessionId}/audio/stream
GET /sessions/{sessionId}/stream
# Tasks
GET /contexts/{contextId}/tasks
POST /contexts/{contextId}/tasks
PUT /tasks/{taskId}
PUT /tasks/{taskId}/status
DELETE /tasks/{taskId}
# Dashboard
GET /dashboard
# User Profile
GET /profile
PUT /profile
# Voice
GET /voice/languages
GET /voice/voices
POST /voice/tts
```
### SSE Event Types
- `message` Complete message
- `messageChunk` Streaming token
- `sessionState` Status update
- `taskCreated` New task from coach
- `insightGenerated` New insight
- `scoreUpdate` Score change
- `status` UI status label
- `complete` Stream ended
- `error` Error
- `ping` Keepalive
## RBAC Model
### Ownership Rules (Critical)
- **Strict MY-only**: User sees only own contexts/sessions/messages/tasks/scores
- **SysAdmin**: Only technical monitoring, NO content access
- **No admin override** on userId filter
### Template Roles
- `commcoach-user`: DATA=MY on all entities, UI=ALL, RESOURCE=ALL
- `commcoach-admin`: DATA=MY (intentionally not ALL), UI=ALL, RESOURCE=ALL
### Audit Events
- `commcoach.context.created/archived`
- `commcoach.session.started/completed`
- `commcoach.export.requested`
## Iterations
### Iteration 1 (MVP)
- Context management (create, switch, archive)
- Chat + SSE streaming
- STT/TTS with language/voice selection
- Coaching session with active diagnostic questions
- Auto session protocol
- Tasks/Checklist per context
- Session summary via email
- RBAC + strict ownership
- Basic dashboard: continuity, competence score, goal progress
- Long-session compression: ab 25 Nachrichten wird der aeltere Verlauf per AI zusammengefasst, letzte 15 Nachrichten bleiben vollstaendig (Teamsbot-Pattern)
- Context Memory (Phasen 1-7): previousSessionSummaries im Chat, keyTopics bei completeSession, Intent-Erkennung (summarize_all, recall_session, recall_topic), Datums-Lookup, Topic-Suche, Rolling Overview, RAG-Platzhalter
### Iteration 2
- Roleplay personas (critical CFO, difficult employee, etc.)
- Document upload + context binding
- Exports (Markdown/PDF)
- Extended gamification (streaks, levels, badges)
- Better scoring/insights
## Database
- Database name: `poweron_commcoach`
- Tables auto-created from Pydantic models via `DatabaseConnector`
## Frontend
### Views
- `CommcoachDashboardView` KPIs, streaks, quick start
- `CommcoachCoachingView` Chat UI with voice + context tabs
- `CommcoachDossierView` Dossier: timeline, tasks, scores
- `CommcoachSettingsView` Voice, reminder, profile settings
### UX
- Multiple active contexts as quick-switch tabs/chips
- "Daily Coach" entry point prominent
- Voice first, always with text fallback
- Dossier view: timeline, learnings, tasks, next exercise

View file

@ -0,0 +1,164 @@
# JSON Continuation Context Module
Ein Python-Modul zur Generierung von Kontextinformationen für abgeschnittene JSON-Strings, um AI-Modellen die Fortsetzung zu ermöglichen.
## Problem
Wenn eine AI-Antwort als JSON abgeschnitten wird (z.B. Token-Limit erreicht), muss die nächste Iteration wissen:
- **Wo** der JSON abgeschnitten wurde
- **Was** bereits generiert wurde
- **Was** als nächstes geliefert werden soll
## Lösung: Drei Kontexte
### 1. Overlap Context
- Zeigt das **innerste Objekt/Array-Element**, das den Cut-Punkt enthält
- Wird verwendet, um den abgeschnittenen Teil mit dem neuen Teil zu **mergen**
- Exakt so wie im Original-String (für String-Matching beim Merge)
### 2. Hierarchy Context
- Zeigt die **hierarchische Struktur** vom Root bis zum Cut-Punkt
- Mit **Budget-Logik**: Näher am Cut = vollständige Werte, weiter weg = `"..."` Platzhalter
- Gibt der AI den Kontext der gesamten JSON-Struktur
### 3. Complete Part (NEU)
- Der **vollständige, valide JSON** bis zum Cut-Punkt
- Alle offenen Strukturen werden geschlossen (`}`, `]`, `"`)
- Unvollständige Keys werden entfernt
- Kann direkt als valides JSON geparst werden
## Installation
```bash
# Keine externen Abhängigkeiten erforderlich
cp json_continuation.py /your/project/
```
## Modulkonstanten
```python
# Diese Konstanten können vor dem Import angepasst werden
BUDGET_LIMIT: int = 500 # Zeichen-Budget für Datenwerte
OVERLAP_MAX_CHARS: int = 1000 # Max Zeichen für Overlap Context
```
## Verwendung
### Grundlegende Verwendung
```python
from json_continuation import extract_continuation_contexts
truncated_json = '''{"customers": [
{"id": 1, "name": "John"},
{"id": 2, "name": "Jane", "email": "jane@exa'''
overlap, hierarchy, complete = extract_continuation_contexts(truncated_json)
print("Overlap Context:")
print(overlap)
# {"id": 2, "name": "Jane", "email": "jane@exa
print("Hierarchy Context:")
print(hierarchy)
# {"customers": [...structure with budget logic...]
print("Complete Part (valid JSON):")
print(complete)
# {"customers": [{"id": 1, "name": "John"}, {"id": 2, "name": "Jane", "email": "jane@exa"}]}
import json
parsed = json.loads(complete) # ✓ Funktioniert!
```
### Mit Dictionary-Interface
```python
from json_continuation import get_contexts
contexts = get_contexts(truncated_json)
print(contexts['overlap'])
print(contexts['hierarchy'])
print(contexts['complete_part'])
```
### Konstanten anpassen
```python
import json_continuation
# Budget anpassen bevor Funktionen aufgerufen werden
json_continuation.BUDGET_LIMIT = 200
json_continuation.OVERLAP_MAX_CHARS = 500
overlap, hierarchy, complete = json_continuation.extract_continuation_contexts(truncated_json)
```
## Rückgabewerte
| Rückgabe | Typ | Beschreibung |
|----------|-----|--------------|
| `overlap` | str | Innerstes Element mit Cut-Punkt (für Merge) |
| `hierarchy` | str | Volle Struktur mit Budget-Logik |
| `complete_part` | str | Valides JSON mit geschlossenen Strukturen |
## Beispiele
### Verschachtelte Objekte
```python
json_str = '{"user": {"profile": {"bio": "Hello Wor'
overlap, hierarchy, complete = extract_continuation_contexts(json_str)
# Overlap: {"bio": "Hello Wor
# Hierarchy: {"user": {"profile": {"bio": "Hello Wor
# Complete: {"user": {"profile": {"bio": "Hello Wor"}}} ← Valides JSON!
```
### Array von Objekten mit unvollständigem Key
```python
json_str = '''{
"items": [
{"id": 1, "name": "First"},
{"id": 2, "name": "Second"},
{"id": 3, "name": "Third", "add'''
overlap, hierarchy, complete = extract_continuation_contexts(json_str)
# Complete entfernt den unvollständigen Key "add":
# {"items": [{"id": 1, ...}, {"id": 2, ...}, {"id": 3, "name": "Third"}]}
```
## Budget-Logik
Die Budget-Logik funktioniert wie folgt:
1. **Sammeln**: Alle String-Werte werden mit ihrer Position gesammelt
2. **Sortieren**: Nach Entfernung zum Cut-Punkt (näher = höhere Priorität)
3. **Zuweisen**: Budget wird von hinten nach vorne aufgebraucht
4. **Ersetzen**: Werte außerhalb des Budgets werden durch `"..."` ersetzt
## Tests ausführen
```bash
python -m unittest test_json_continuation -v
```
## API Referenz
### `extract_continuation_contexts(truncated_json: str) -> Tuple[str, str, str]`
Hauptfunktion. Gibt `(overlap, hierarchy, complete_part)` zurück.
### `get_contexts(truncated_json: str) -> dict`
Convenience-Funktion. Gibt Dictionary mit Keys `'overlap'`, `'hierarchy'`, `'complete_part'` zurück.
### Modulkonstanten
- `BUDGET_LIMIT`: int (default: 500) - Zeichen-Budget für Hierarchy-Context
- `OVERLAP_MAX_CHARS`: int (default: 1000) - Max Zeichen für Overlap-Context

View file

@ -204,7 +204,7 @@ bereit und lässt ihn einen Graphen erzeugen → als Draft-Version speichern →
## Betroffene Module (Skizze)
- **Gateway:**
- `features/graphicalEditor/` — dünnes `Solution`-Modell (`mandateId`/`workflowRef`/`runAsPrincipal`/`surfaceFeatureRef`/`settingsValues`/`instanceSelection`/`outputBinding`/`customerFacing`) + Solution-Service; `AutoWorkflow` selbst bleibt schlank (Konfiguration via `trigger.form`). **DB-Migration: ja (Greenfield, additiv).** Details: step3-features-plan A0/A1.
- `features/graphicalEditor/` **wird zur System-Komponente `WorkflowAutomation`** umgebaut (Voraussetzung, eigener Plan `c-work/1-plan/2026-06-automation-system-component.md`); danach dünnes `Solution`-Modell (`mandateId`/`workflowRef`/`runAsPrincipal`/`surfaceFeatureRef`/`settingsValues`/`instanceSelection`/`outputBinding`/`customerFacing`) + Solution-Service; `AutoWorkflow` bleibt schlank (Konfiguration via `trigger.form`). **DB-Migration: ja.** Details: step3-features-plan A0/A1.
- `workflows/scheduler/mainScheduler.py` — unverändert nutzbar (Trigger-Policy mappt auf bestehende Schedule-Logik).
- `interfaces/interfaceBootstrap.py` — Use-Case-Katalog als System-Templates.
- `serviceCenter/services/serviceAgent/` (Toolbox `workflow`) — Use-Case→Graph-Generierung; ggf. Prompt/Guardrails ergänzen.

View file

@ -97,12 +97,12 @@ Solution {
→ Es ist «n8n in PORTA», kein Domänen-Feature wie Trustee. Vergleich: Scheduler/RBAC/`system` sind reine Infrastruktur; `neutralization` ist das echte Feature-Analogon (DATA, kein `onStart`).
**Entscheid (pragmatisch, kein Big-Bang):**
**Entscheid (vorgezogen — eigener Plan):** `graphicalEditor` wird **von einem Feature zur System-Komponente `WorkflowAutomation`** umgebaut, **bevor** die Solution-Schicht gebaut wird (sonst erbt sie den Designfehler). `graphicalEditor` ist darin nur **ein Modul** (der Editor). Details, Phasen, DB-/UI-/RBAC-Migration: **`c-work/1-plan/2026-06-automation-system-component.md`**.
- **Konzeptionell als Plattform-/System-Komponente führen** («Automation/Workflow-Plattform»), nicht als Domänen-Feature. Der **Feature-Mantel bleibt** als RBAC-Eintritt + Store-Packaging + Navigation — das ist legitime Wiederverwendung der Feature-Mechanik, kein Modellfehler.
- **Solutions nicht an einen GE-Owner hängen** (siehe A0.1): Solution = mandatsweit + Run-as-Principal; die GE-Instanz ist nur Authoring-/RBAC-Eintritt.
- **Doku/Mentales Modell schärfen:** `b-reference` soll `graphicalEditor` explizit als **«Orchestrierungs-Substrat, als Feature paketiert»** beschreiben, damit künftige Designs nicht erneut einen «Host-Feature-Owner» erfinden.
- **Roadmap (optional, nicht jetzt):** Scheduler-Boot vom Feature-`onStart` entkoppeln (echter System-Service) und GE-Instanz rein als Authoring-Surface führen. Erst ziehen, wenn es weh tut (YAGNI).
- **Eigene Top-Level-Nav-Gruppe «Workflow-Automation»** (Tab-Seiten: Editor · Vorlagen · Workflows · Läufe · Tasks), mandanten-/feature-übergreifend an einem Ort — **nicht** als Feature-Seite.
- **Scheduler-Boot** in System-Lifespan (statt Feature-`onStart`); **API mandatsweit** `/api/workflow-automation/…` (RBAC = Mandats-Mitgliedschaft + System-Rollen + `isPlatformAdmin`).
- **DB:** `AutoWorkflow.featureInstanceId` verliert RBAC-Bedeutung; `mandateId`+`runAsPrincipal` = Owner; `targetFeatureInstanceId`/per-Node-Ref bleiben (Daten-Scope).
- **Solutions hängen damit sauber an Mandant + Run-as-Principal** (A0.1), nicht an einer GE-Instanz. Die kundenseitige «Lösungen»-Surface bleibt pro Host-Feature eingebettet (Präsentation) — getrennt von der technischen WorkflowAutomation-Gruppe.
> Netto (korrigiert): **A0.1** verankert Ownership an **Mandant + Run-as-Principal** (statt erfundenem Host-Owner); **A0.2** behandelt Multi-Feature/Multi-Instanz als **native** Fähigkeit und benennt die 3 echten Code-Lücken (Runtime-Mandatscheck, Read-RBAC, per-Node-Billing); **A0.3** hält «Config statt Code» mit der Versions-Invariante vereinbar; **A0.4** ordnet `graphicalEditor` als Orchestrierungs-Substrat ein. Alles auf bestehenden Mechanismen (`mandateId`-Scoping, `FeatureInstanceRef`, `runEnvelope`/`trigger.form`, `recordUsage`).
@ -229,7 +229,7 @@ Echtes Feature (eigene Modelle + opinionated UI), das die Plattform-Bausteine b
| 2026-06-05 | **A0.2 (korrigiert)** Multi-Feature/Multi-Instanz ist **nativ** (per-Node `FeatureInstanceRef`); Schranke = Mandant | Code-Befund: Picker mandats-gescoped, kein Sonder-Fan-out nötig |
| 2026-06-05 | **A0.2** 3 Code-Lücken schliessen: Runtime-Mandatscheck, Read-RBAC, **per-Node-Billing** | heute Billing fix auf `graphicalEditor`; Reads ohne RBAC; Mandat nur design-time geprüft |
| 2026-06-05 | **A0.3** Settings als **Run-Envelope-Injektion** (`trigger.form`/DataRef), nie Graph-Mutation | hält Published-Version-Invariante; 1 Template-Graph, N Settings |
| 2026-06-05 | **A0.4** `graphicalEditor` als **Orchestrierungs-Substrat** führen (Feature-Mantel bleibt) | Code-Befund: kein DATA, eigene DB für alle Features, globaler Scheduler-Boot |
| 2026-06-05 | **A0.4 (vorgezogen)** `graphicalEditor` von Feature → System-Komponente **`WorkflowAutomation`** umbauen (eigener Plan) | Code-Befund: kein DATA, eigene DB für alle Features, globaler Scheduler-Boot; Solution-Schicht würde Designfehler erben → `c-work/1-plan/2026-06-automation-system-component.md` |
| 2026-06-05 | Versand als **neuer `email.sendEmail`-Node** (heute nur `draftEmail`) | Send-Node fehlt real → Blocker für Reporting-Solutions |
| 2026-06-05 | i18n: UI via `t()`, kundensichtbare Inhalte als `TextMultilingual` | `ui-nyla`-Regel; Kataloge/Settings lokalisierbar |
| 2026-06-04 | Plattform-Bausteine + Solution-Schicht als **generischer Code**, nie kundenspezifisch | macht alle Solutions möglich, hält «kein Kunden-Code» real |
@ -245,7 +245,7 @@ Echtes Feature (eigene Modelle + opinionated UI), das die Plattform-Bausteine b
- [ ] A0.1 **Run-as-Principal** in Engine + Scheduler (statt globalem `event`-Sysadmin) + Read-RBAC gleichziehen
- [ ] A0.2 **Runtime-Mandatsvalidierung** beim `FeatureInstanceRef`-Auflösen + **per-Node-Billing** (`recordUsage` mit berührter Instanz) + Klarstellung in `feature-instance-scoping.mdc`
- [ ] A0.3 Run-Envelope-Injektion aus `settingsValues` (kein Graph-Schreiben) + `settingsSchema`-Drift-Validierung + optional `pinnedVersionId`
- [ ] A0.4 `b-reference`: `graphicalEditor` als Orchestrierungs-Substrat dokumentieren (Roadmap: Scheduler-Boot entkoppeln — später)
- [ ] A0.4 **System-Komponente `WorkflowAutomation` bauen ZUERST** (eigener Plan `c-work/1-plan/2026-06-automation-system-component.md`): Scheduler-Boot entkoppeln, Nav-Gruppe «Workflow-Automation», mandatsweite API/RBAC, DB-Migration
**Teil A (Enabler):**
- [ ] Solution-Routes **am Host-Feature** (CRUD/Test/Activate) + Host-RBAC + `SolutionsView` (Tab-Pattern, FormGenerator, `useConfirm`/`usePrompt`, i18n)
@ -294,7 +294,7 @@ Echtes Feature (eigene Modelle + opinionated UI), das die Plattform-Bausteine b
5. **Lawyer-Connectors:** iManage/KYC/Konflikt-Check-APIs — welche mandantenspezifisch, welche generisch?
6. **Connector-Generalisierung:** wann Capability-Mixins (`AccountingCapable`/`PayrollCapable`) ziehen?
7. **`email.sendEmail`-Scope:** eigener Node vs. `email.draftEmail` + Auto-Send-Flag — und Verhältnis zu `input.emailWait`/`testMode`.
8. **`graphicalEditor`-Entkopplung:** Scheduler-Boot vom Feature-`onStart` lösen — jetzt oder als spätere Roadmap (A0.4)?
8. **`graphicalEditor`-Entkopplung:** ✅ entschieden — **jetzt**, als eigener Plan `c-work/1-plan/2026-06-automation-system-component.md` (Voraussetzung für die Solution-Schicht).
## Links

View file

@ -0,0 +1,284 @@
<!-- status: plan -->
<!-- started: 2026-06-05 -->
<!-- component: gateway | ui-nyla | platform -->
# 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: `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 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.py``NAVIGATION_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.py``TABLE_NAMESPACE` (8 Einträge: `AutoWorkflow/AutoVersion/AutoRun/AutoStepLog/AutoTask` + Legacy `Automation2*`): **Option B** — Namespace `feature.graphicalEditor` **beibehalten** + System-DATA-Objekte ergänzen. (Ein Flip auf `system.workflowAutomation` verwaist bestehende `AccessRule`/Rollen-Zeilen; nur mit zwingend gekoppeltem Migrations-Skript im selben Deploy.)
- `modules/interfaces/interfaceBootstrap.py` — Store-Grant + ggf. neue System-Rollen `workflowAutomation-*`.
- `modules/features/graphicalEditor/mainGraphicalEditor.py` `getGraphicalEditorServices``featureCode`/`featureInstanceId` im 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` (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.tsx``block.id === 'workflowAutomation'` als eigene Top-Level-Gruppe rendern (analog `admin`).
- `src/App.tsx``/workflow-automation/*`-Routen (Hub + `?tab=` Deeplinks); Redirects der alten `/mandates/.../graphicalEditor/.../*`.
- `src/pages/AutomationsDashboardPage.tsx` → in **`WorkflowAutomationHubPage`** mit Tabs überführen (Workflows, Läufe, Editor, Vorlagen, Tasks).
- `src/pages/FeatureView.tsx``graphicalEditor` aus `VIEW_COMPONENTS` entfernen.
- `src/pages/views/graphicalEditor/*` — von `useInstanceId()`/`useCurrentInstance()` auf expliziten Mandanten-/Instanz-Picker (Scope-Selector) umstellen.
- `src/config/keepAliveRoutes.tsx` — Editor-Keep-Alive-Pfad auf `/workflow-automation/editor` umziehen.
- `src/config/pageRegistry.tsx` — Icons für `page.system.workflowAutomation.*`.
- `src/api/workflowApi.ts` — URLs `/api/workflows/{instanceId}/…``/api/workflow-automation/…`.
- Cross-Links: `TrusteeAnalyseView`/`TrusteeAbschlussView` (`/automations?tab=…`), `Store.tsx` (GE-Karte entfernen).
- **DB-Migration:** minimal — `runAsPrincipal` = additives `ADD COLUMN`; `featureInstanceId` ist **bereits DB-nullable** (kein DDL, keine DB-FK; nur Pydantic `Optional` + Code); Daten-/Grant-Backfill via Skript. **Kein** Namespace-Flip (Option B).
- **Andere:** RBAC (System-Objekte/-Rollen, FeatureAccess-Grants migrieren, GE-Feature-Rollen entfallen), Billing (per-Node-Attribution), Demo-Configs (`pwgDemo2026`/`investorDemo2026`), Tests, `.cursor/rules/*` (s. Abschluss).
## Entscheidungen
| Datum | Entscheidung | Begründung |
|-------|-------------|------------|
| 2026-06-05 | System-Komponente heisst **`WorkflowAutomation`** (Token `WorkflowAutomation`/`workflowAutomation`); UI-Label «Workflow-Automation» | semantisch eindeutig + greppbar; generisches «Automation» bleibt für Bestand |
| 2026-06-05 | `graphicalEditor` = **ein Modul** (editor) innerhalb von WorkflowAutomation | graphEditor ist nur das Authoring-Element |
| 2026-06-05 | **Automation ≠ Workflow-Execution.** `methods`/`processing` bleiben geteilter Layer unter `modules/workflows/`; nur `automation2` (engine) + `scheduler` + `graphicalEditor` wandern in die Komponente | Chats/Agents nutzen den Execution-Layer **ohne** Automation; Abwärts-Abhängigkeit Komponente→Execution (Code-belegt) |
| 2026-06-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.featureInstanceId``Optional` (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.
## 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.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/`

View file

@ -14,6 +14,9 @@ Skip: reine Refactors, Formatting, Lint, Dep-Bumps, Test-only, Wiki-Tippfehler.
## 2026-06-05
- 2026-06-05 | docs | wiki | **WorkflowAutomation-Plan nach 2 kritischen Code-Reviews überarbeitet (+ Trennung Automation ↔ Execution)**: Zentrale Korrektur — **Automation** (Graph-Editor/Engine `automation2`/Scheduler/Solutions) und der geteilte **Workflow-Execution-Layer** (`workflows/methods`+`processing`, von reinen Chats/Agents genutzt) bleiben im Code getrennt; nur die Automation-Teile wandern in die Komponente. Frühere Aussage «`automation2` wird von Chats nicht importiert» als **falsch** korrigiert (shared `methods` importieren `automation2.executors`) → neue **Phase 0.5 Delaminierung** (`PauseForHumanTaskError`/`_coerce_document_data_to_bytes` + `portTypes`/`STATIC_NODE_TYPES` → neues geteiltes `workflows/workflowContracts/`). Weitere Review-Fixes: `featureInstanceId` ist **bereits DB-nullable** (kein DDL, keine DB-FK), Scheduler ist hart auf `featureInstanceId` gekeyt (Entkopplung als eigener Schritt), Dashboard-RBAC deckt nur Lesen → eigener Write-Helper `_validateWorkflowAccess` für 39 Sites, RBAC-Namespace **Option B** (kein Flip), Template-Instanziierungs-Trigger (`_copyTemplateWorkflows`) wandert in `launcher/`, per-Node-Billing als bewusste Executor-Ausnahme, `.cursor/rules/*` aktualisieren. (c-work: c-work/1-plan/2026-06-automation-system-component.md)
- 2026-06-05 | docs | wiki | **Plan: System-Komponente `WorkflowAutomation` (`graphicalEditor` raus aus dem Feature-Modell)**: Neuer Umsetzungsplan in `c-work/1-plan/`. Komponente heisst `WorkflowAutomation` (greppbares Token; `graphicalEditor` = ein Modul darin), modulares Layout (toolbox/editor/engine/scheduler/solutions/launcher/monitoring) gemappt auf L1L4. Code-fundiert (Discovery/Registry, `FeatureInstance`-Lifecycle, DB `poweron_graphicaleditor`, Scheduler-Boot via Feature-`onStart`, RBAC-Präzedenz `routeWorkflowDashboard`/`routeAutomationWorkspace`, ~70 Backend-Touchpoints + UI-Navigation). Phasen 04: Scheduler-Boot in System-Lifespan → mandatsweite API `/api/workflow-automation/…` + System-RBAC → Top-Level-Nav-Gruppe «Workflow-Automation» mit Tab-Seiten → DB (`runAsPrincipal`, `featureInstanceId` als RBAC-Anker abbauen, Daten-Backfill) → Feature-Mantel entfernen. Vorgezogen aus A0.4, weil Voraussetzung für die Solution-Schicht. (c-work: c-work/1-plan/2026-06-automation-system-component.md)
- 2026-06-05 | docs | wiki | **CustomerCases A0.4 von Roadmap auf «jetzt» gezogen**: step1-architecture + step3-features-plan verweisen für die `graphicalEditor`→System-Komponente-Umstellung auf den neuen 1-plan; offene Frage #8 + Checkliste/Entscheidungstabelle entsprechend aktualisiert. (c-work: c-work/0-ideas/2026-06-CustomerCases-step3-features-plan.md)
- 2026-06-05 | docs | wiki | **A0 Code-verifiziert korrigiert: Workflow-Ownership = Mandant + Run-as-Principal (nicht Host-Feature)**: Code-Analyse (`executionEngine.py`, `mainScheduler.py`, `routeFeatureGraphicalEditor.py`, `datamodelFeatureGraphicalEditor.py`, Trustee-Actions) zeigt, dass Workflows mandatsweit + identitätsgebunden über beliebige Feature-Instanzen laufen — kein Host-Feature-Owner. **A0.1** neu: Owner-Achse = `mandateId` + `runAsPrincipal`, RBAC = Principal-RBAC; scheduled Runs sollen unter definierter Automations-Identität statt globalem `event`-Sysadmin laufen. **A0.2** neu: Multi-Feature/Multi-Instanz ist nativ (per-Node `FeatureInstanceRef`), echte Schranke = Mandant; 3 Code-Lücken benannt (Runtime-Mandatscheck beim Ref-Auflösen, Read-RBAC analog Write, per-Node-Billing statt fix `graphicalEditor`). **A0.4** neu: `graphicalEditor` als Orchestrierungs-Substrat eingeordnet (kein DATA, eigene DB für alle Features, globaler Scheduler-Boot via `onStart`) — Feature-Mantel bleibt als RBAC-/Store-Eintritt. step1-architecture + product-summary angeglichen. (c-work: c-work/0-ideas/2026-06-CustomerCases-step3-features-plan.md)
- 2026-06-05 | docs | wiki | **CustomerCases step3-Pläne überarbeitet + 3 Architektur-Leitentscheide (A0)**: Nach kritischem Review beide Pläne geschärft. Neuer Abschnitt **A0** im features-plan löst die 3 kritischen Punkte: **A0.1** Solution = `graphicalEditor`-Execution mit `hostFeatureRef` als alleinigem Owner für RBAC/Billing (Cross-Feature-Drift gelöst); **A0.2** Multi-Instanz-Fan-out nur mandatsintern über explizites `instanceSet`, instanz-genaue Billing-Attribution; **A0.3** Settings als Run-Envelope-Injektion (`trigger.form`/DataRef) statt Graph-Mutation (Published-Version-Invariante gewahrt). Zusätzlich: Node-IDs gegen `nodeDefinitions/` verifiziert (neuer `email.sendEmail`-Versand-Node + `data.writeToTable` als Blocker erkannt; `data.consolidate` vorhanden), neuer i18n-Abschnitt (A5, `t()` + `TextMultilingual`), RBAC-Items/Deletion-Cascade/Adapter-Drift ergänzt, S5 als «vorhandene Templates exponieren» (statt migrieren) korrigiert. step1-architecture + product-summary + mockup auf neue Entscheide/Node-IDs angeglichen. (c-work: c-work/0-ideas/2026-06-CustomerCases-step3-features-plan.md, 2026-06-CustomerCases-step3-solutions-plan.md)