wiki/c-work/0-ideas/2026-06-CustomerCases-step3-features-plan.md

312 lines
36 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<!-- status: plan -->
<!-- started: 2026-06-04 -->
<!-- component: gateway | ui-nyla | platform -->
<!-- replaces: 2026-06-STEP2-lawyer-feature.md, 2026-06-STEP2-umsystem-integration-connectors-und-datenquellen-seiten.md (Code-Teile), 2026-06-STEP2-pm-consolidated-customer-requirements.md (Feature/Plattform-Teile) -->
# Plan: Features & Plattform-Bausteine — der Code hinter den Solutions
> **Baut auf:** `2026-06-CustomerCases-step1-architecture.md` (Schichten L1L4, Feature-vs-Solution-Grenze).
> **Schwester-Plan:** `2026-06-CustomerCases-step3-solutions-plan.md` (die konfigurierten Use Cases, die diesen Code konsumieren).
> **Konsolidiert/ersetzt:** Lawyer-Feature-Plan + die Code-/Plattform-Teile aus Umsystem-Integration und konsolidiertem Kundenwünsche-Plan.
## Beschreibung und Kontext
Ein **Feature** ist **Code**: eigenes Datenmodell und/oder tiefe, opinionated UI / Domänenlogik, die ein Graph nicht ausdrücken kann (step1-architecture, Entscheidungsmatrix). Dazu zählen auch die **Plattform-Bausteine**, die die Solutions erst möglich machen — sie sind generischer Code (L1/L2/L3/L4), **ohne Kundennamen**.
Dieser Plan bündelt allen **Code**, der zu bauen ist, in zwei Teilen:
- **Teil A — Plattform-Enabler:** die Solution-Schicht selbst (L3/L4), generische Workflow-Bausteine, Connectors, das «Datenquelle = Seite»-Muster. Voraussetzung für *alle* Solutions.
- **Teil B — Vertikale Code-Features:** Module mit eigenem Datenmodell/UI (`lawyer`), bestehende Features (`commcoach`, `trustee`, `neutralization`) und ihre Erweiterungen.
**Leitsatz (step1-architecture):** Solution-first. Code bauen wir nur, wenn (a) es ein generischer Baustein ist, den viele Solutions teilen, oder (b) der Use Case nachweislich nicht als Solution trägt.
## Fokus und kritische Details
- **Generisch, nie kundenspezifisch.** Connectors = reine API-Adapter (Auth + Lesen/Schreiben + Mapping auf kanonische Modelle), keine Geschäftsregeln, keine Kundennamen. Kundenlogik lebt als Solution (Daten).
- **Eine Workflow-Wahrheit.** Die Solution-Schicht ist eine **dünne Erweiterung der bestehenden `graphicalEditor`-Welt** (`AutoWorkflow`/`AutoVersion`/`AutoRun`/`WorkflowScheduler`), keine Parallel-Engine. Die tragenden Architektur-Entscheide dazu stehen in **A0** (Ownership = Mandant+Run-as-Principal, native Multi-Feature/Multi-Instanz, Settings-Injektion, `graphicalEditor`-Einordnung) — Voraussetzung für alles Weitere.
- **Source vs. Target** ist die wichtigste neue Connector-Achse (macht aus dem Push-Framework ein bidirektionales Integrations-Framework).
- **RBAC strikt feature-instance-scoped**, nicht mit Mandate-Rollen vermischen (`.cursor/rules/rbac-role-separation.mdc`). Admin-Match immer `roleLabel.endswith("-admin")`, nie `"admin" in label`.
- **i18n-Pflicht:** jeder neue UI-Text via `t('Deutscher Klartext')` (nur String-Literale), kundensichtbare Inhalte (Solution-Name/-Beschreibung, kuratierte Settings-Labels) als `TextMultilingual` — Details in **A5**.
- **Adapter-Drift-Invariante:** jeder neue Node muss `test_adapter_validator.py` (`_KNOWN_ADAPTER_DRIFTS = frozenset()`) grün halten.
- **Neutralisierung + Private LLM** zwingend für jeden LLM-Pfad mit Personendaten (Trustee, Lawyer/Berufsgeheimnis StGB 321).
- **YAGNI bei Connector-Abstraktion:** beim bestehenden `BaseAccountingConnector` bleiben und nur die **Source-Rolle** ergänzen; das generische «Capability-Mixin»-Framework erst ziehen, wenn die zweite Kategorie (Saläre) real wird.
## Teil A — Plattform-Enabler (macht Solutions möglich)
### A0 — Architektur-Leitentscheide (die kritischen Punkte)
> **Korrektur ggü. erstem Entwurf (Code-verifiziert, 2026-06-05).** Eine Code-Analyse von `executionEngine.py`, `mainScheduler.py`, `routeFeatureGraphicalEditor.py`, `datamodelFeatureGraphicalEditor.py` und den Trustee-Actions zeigt: Die Plattform modelliert Workflows **nicht** als Eigentum eines Host-Features, sondern als **mandatsweite, identitätsgebundene Orchestrierung über beliebige Feature-Instanzen**. Der frühere Entwurf «Owner = `hostFeatureRef`» (instanz-genaues Billing, Fan-out als Ausnahme) war **falsch geschnitten**. A0.1/A0.2 sind unten korrigiert; A0.3 (Settings-Injektion) bleibt gültig; A0.4 ordnet `graphicalEditor` ein.
#### A0.1 — Ownership-Achse: **Mandant + Run-as-Principal**, nicht Host-Feature
**Befund (Code):** `AutoWorkflow` trägt `mandateId` (Tenant-Grenze), `featureInstanceId` (GE-Instanz = nur RBAC-/Authoring-Eintritt) und `targetFeatureInstanceId` (Default-Daten-Scope). `getWorkflows()` filtert **nur nach `mandateId`** («cross-instance»). Ein Workflow läuft unter `services.user` — interaktiv der eingeloggte User, im Scheduler der **`event`-Service-Account** (`isSysAdmin=True`). `AutoRun.ownerId` = auslösender User. Es gibt **keinen** Host-Feature-Owner; ein Graph referenziert legitim **mehrere** Features (`trustee.*`, `redmine.*`, …) je per eigenem `FeatureInstanceRef`.
**Konsequenz — die richtige Achse:** Eine **Solution** ist ein **mandatsweit gescopter, konfigurierter Workflow**, der unter einem **Run-as-Principal** läuft; Zugriff auf Feature-Daten richtet sich nach der **RBAC dieses Principals**, über Feature-Grenzen hinweg. «Im Trustee angezeigt» ist **Präsentation**, nicht Eigentum.
```
Solution {
id
mandateId # Tenant-Grenze (Owner-Achse #1)
workflowRef -> AutoWorkflow (Orchestrierungs-Substrat)
runAsPrincipal # in wessen Namen ausgeführt wird (Owner-Achse #2):
# - interaktiv: auslösender User
# - geplant: definierte Automations-Identität (NICHT global sysadmin)
surfaceFeatureRef? # NUR UI-Platzierung (z.B. trustee-Seite), kein Governance-Kontext
settingsValues # Daten (A0.3), Secrets verschlüsselt
instanceSelection? # Menge der zu verarbeitenden Feature-Instanzen (A0.2)
customerFacing, outputBinding
}
```
- **RBAC = Principal-RBAC, nicht Host-Rollen.** Verwaltung der Solution wird über die bestehende **Workflow-/Automation-Permission** autorisiert; Host-Features exponieren das wie heute Trustee via RESOURCE-Item, das auf die Workflow-API zeigt (`resource.feature.trustee.workflows.manage` → `/api/workflows/{instanceId}/…`). Kein Bedarf, einen «Host-Owner» zu erfinden.
- **Run-as-Principal explizit machen (Code-Lücke schliessen):** Heute laufen geplante Runs als globaler `event`-Sysadmin → effektiv RBAC-Bypass. Sauber: pro Solution eine **definierte Automations-Identität** (Ersteller oder dedizierter Service-Principal mit *genau* den nötigen Feature-Rollen), in deren Namen der Scheduler ausführt. «Der Ersteller bestimmt, in wessen Namen es läuft» (deine Vorgabe) wird damit ein First-Class-Feld.
- **Daten-Lesezugriff RBAC-gleichziehen (Code-Lücke):** Trustee-**Writes** prüfen `checkCombinedPermission`, **Reads** filtern heute nur nach `featureInstanceId` ohne RBAC. Für «Daten sind gemäss RBAC verfügbar» müssen Lesepfade dieselbe Principal-RBAC anwenden (sonst sieht der Principal mehr als er dürfte).
#### A0.2 — Multi-Feature-/Multi-Instanz-Zugriff ist **nativ**, die echte Schranke ist der Mandant
**Befund (Code):** Pro-Node-`FeatureInstanceRef` ist der etablierte Mechanismus; der Instanz-Picker ist **mandats-gescoped** (`getFeatureInstancesByMandate`, filtert nach `featureCode`) und «leakt nie fremde Mandanten». Ein Graph über 5 Trustee-Instanzen **eines** Mandanten ist also kein Sonderfall, sondern der Normalfall (Pling = 5 Trustee-Instanzen eines Mandanten — bestätigt).
**Konsequenz:**
- **Kein Sonder-`instanceSet` als Ausnahme.** Multi-Instanz-Verarbeitung ist die native Fähigkeit (`flow.loop` über eine mandatsinterne Instanz-Auswahl, je Iteration ein `FeatureInstanceRef`). `feature-instance-scoping.mdc` braucht **keine** Fan-out-Ausnahme, sondern eine **Klarstellung**: *Cross-Instanz-Verarbeitung innerhalb eines Mandanten ist erlaubt, sofern der Run-as-Principal auf den Instanzen berechtigt ist; Cross-Mandate ist verboten.*
- **Laufzeit-Schranke schliessen (Code-Lücke):** Die Mandatsgrenze wird heute nur **design-time** (Picker) und am API-Start (`_validateTargetInstance`) geprüft, **nicht** pro Node zur Laufzeit. Ein handgebauter Graph könnte eine fremd-mandatige Instanz-UUID einschleusen. → **Runtime-Validierung:** beim Auflösen jedes `FeatureInstanceRef` prüfen, dass die Instanz zum `mandateId` des Runs gehört **und** der Run-as-Principal `FeatureAccess` hat.
- **Billing korrekt attribuieren (Code-Lücke, ersetzt den alten Vorschlag):** Heute bucht **jede** Nutzung auf `featureCode="graphicalEditor"` + GE-Instanz (aus dem Service-Hub), **unabhängig** von der berührten Trustee-Instanz. Mein früherer Satz «instanz-genaues Billing existiert» war falsch. Sauber: `recordUsage` soll, wenn ein Node eine `FeatureInstanceRef` trägt, **diese** `featureInstanceId`/`featureCode` mitschreiben (per-Node-Attribution); Mandate bleibt korrekt (`mandateId`). So werden Store-Kosten real dem Store, Holding-Overhead der Holding zugeordnet — über die bestehende `recordUsage`-Signatur, ohne neue Billing-Achse.
- **RBAC der Empfänger:** `rbac.queryUsersByRole` löst **pro iterierter Instanz** auf (Store-Empfänger je Store).
#### A0.3 — Settings-Injektion statt Graph-Mutation (löst die Versions-Invariante)
**Problem:** «Settings = Daten, kein Deploy» darf den **immutablen, publizierten** `AutoVersion`-Graph nicht verändern (Invariante: max. eine PUBLISHED-Version; Scheduler bindet an Published).
**Lösung — Settings werden zur Laufzeit als Inputs injiziert, nie in den Graph geschrieben:**
- Das Template deklariert seine konfigurierbare Oberfläche über einen **`trigger.form`**-Node (bzw. als «exposed» markierte Params). `settingsSchema` wird daraus abgeleitet (`parse_graph_defined_output_schema` / `{kind:"fromGraph"}` existiert real).
- Die `Solution` speichert nur **`settingsValues`** (Daten). Beim Run-Start baut der Solution-Service das **`runEnvelope`** aus `settingsValues` und speist sie als `trigger.form`-Output ein. Die Graph-Nodes lesen sie via **DataRef** (Pick-not-Push, bestehender Mechanismus). **Der publizierte Graph bleibt unangetastet.**
- **Ein Template-Graph, N Solutions:** N Kunden = N `settingsValues`-Records auf **einer** publizierten Version. Keine Graph-Kopien pro Kunde, kein Re-Publish bei Settings-Änderung.
- **Secrets** (Connector-Credentials) liegen verschlüsselt in `settingsValues` (wie `TrusteeAccountingConfig`) und werden nur ins `runEnvelope` injiziert, nie in den Graph persistiert.
- **Template-Versionierung entkoppelt:** Solutions referenzieren die `PUBLISHED`-Version des Templates; optional `pinnedVersionId` zum Einfrieren. Bei neuem `settingsSchema` werden bestehende `settingsValues` beim Laden gegen das neue Schema validiert (Migrations-Hinweis in der UI).
#### A0.4 — `graphicalEditor` ist ein **Orchestrierungs-Substrat** (System-Komponente) im Feature-Mantel
**Befund (Code):** `graphicalEditor` ist ein **Hybrid**. Formal ist es ein Feature (Discovery wie Trustee, `TEMPLATE_ROLES` `graphicalEditor-viewer/user/admin`, Store-Eintrag `resource.store.graphicalEditor`, `FeatureInstance` pro Mandant). Architektonisch ist es **System-Infrastruktur**:
- **Kein `getDataObjects()`** — keine End-Kundendaten/Domäne; `AutoWorkflow`/`AutoRun` sind Orchestrierungs-Metadaten. Es ist das einzige Feature **ohne** DATA-Katalog.
- **Eigene DB `poweron_graphicaleditor`** hält Graphen/Runs **aller** Features; Trustee-System-Templates werden bei Trustee-Anlage **dorthin** kopiert (`targetFeatureInstanceId` = Trustee-Instanz).
- **Einziges Feature mit `onStart`/`onStop`** — bootet beim App-Start den **globalen** Scheduler + Email-Poller (für alle Mandanten), unabhängig davon, ob ein Mandant eine GE-Instanz hat.
- **Scheduler scannt mandatsübergreifend** (`getAllWorkflowsForScheduling()` ohne Mandatsfilter); System-Dashboard `/api/system/workflow-runs` ist eine **Ops-Sicht** über alle Mandanten.
- Engine + Scheduler liegen ohnehin unter `modules/workflows/` (System), nicht im Feature.
→ 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 (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`**.
- **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`).
### A1 — Solution-Schicht (L3/L4)
- **`Solution`-Modell** in `graphicalEditor` (siehe A0.1): `mandateId`, `workflowRef`, `runAsPrincipal`, `surfaceFeatureRef?` (nur UI-Platzierung), `settingsValues` (Secrets verschlüsselt), `instanceSelection?`, `customerFacing`, `outputBinding`, optional `pinnedVersionId`. **DB-Migration: ja (Greenfield, additiv).** `AutoWorkflow` selbst bleibt schlank; konfigurierbar über `trigger.form` (A0.3) — **kein** Settings-Schreiben in den Graph.
- **`settingsSchema` automatisch ableiten** aus `trigger.form` + als «exposed» markierten Node-Params (`parse_graph_defined_output_schema`); kuratierte, mehrsprachige Label-Overrides via A5.
- **Run-Start = Settings-Injektion** (A0.3): Solution-Service baut `runEnvelope` aus `settingsValues`, Nodes lesen via DataRef. Ausführung unter `runAsPrincipal` (A0.1). **Billing per-Node** auf die berührte `FeatureInstanceRef` (A0.2), Mandate = `mandateId`.
- **Solution-Routes** (dünn über bestehende Editor-/Run-Routes): CRUD Settings, Test-Run (`testMode`), Activate/Deactivate. Verwaltungs-RBAC = **Workflow-/Automation-Permission** (vom Host-Feature via RESOURCE-Item auf die Workflow-API exponiert, bestehendes Trustee-Muster `resource.feature.trustee.workflows.manage`), **nicht** ein erfundener Host-Owner.
- **`SolutionsView`** (ui-nyla): **eine** generische «Lösungen»-Seite, pro Host-Feature als **Surface eingebettet** (Präsentation, nicht Ownership) — Liste · Katalog/Neu · Detail mit Tabs (Einstellungen · Testlauf · Läufe · Ausgabe). Folgt dem Tab-Pattern (`?tab=`/`?solutionId=`-Deep-Links, Lazy-Mount, Flex-Chain) wie `TrusteeDataTablesView`. Settings-Formular aus `settingsSchema` via `FRONTEND_TYPE_RENDERERS`/FormGenerator (Felder **nie** hardcodiert). Aktionen über `useConfirm()`/`usePrompt()`. Registriert via `FeatureView.tsx` + UI-Areas in `main<Feature>.py`. Alle Texte i18n (A5).
- **Use-Case-Katalog** = `templateScope=system`-Workflows mit `customerFacing=true`, gefiltert nach Host-`featureCode`/Tag (bestehende System-Template-Mechanik in `mainGraphicalEditor._bootstrapSystemTemplates` / `interfaceBootstrap.py`).
- **AI: Use Case → Workflow** über bestehenden Workflow-Agent (Toolbox `workflow`), Draft + Review vor Aktivierung.
- **Output-Renderer** für `outputBinding.kind` = `file` (Datenraum/`TrusteeDocument`), `table` (FormGenerator/Data-Tables, Spalten aus Node-Output-Schema), `summary` (`AutoRun`/`AutoStepLog`). `dashboard` später (AI-Report/Canvas-Pipeline).
### A2 — Generische Workflow-Bausteine (Nodes/Actions)
> **Node-ID-Abgleich gegen `nodeDefinitions/` (verifiziert).** Vorhanden: `trustee.queryData` (echter Node), `data.consolidate`, `data.aggregate`, `data.filter`, `ai.prompt`, `ai.generateDocument`, `ai.consolidate`, `file.create`, `sharepoint.listFiles`/`sharepoint.readFile`/`sharepoint.upload`, `flow.loop`, `trigger.*`. **Es gibt KEINEN Versand-Node** — nur `email.checkEmail`/`email.searchEmail`/`email.draftEmail` (Senden existiert nur als Methode `outlook.sendDraftEmail`). **Es gibt KEIN `data.writeToTable`.** Beides muss gebaut werden (unten).
| Baustein | Zweck | Status |
|---|---|---|
| `email.sendEmail` (Versand-Node) | Mail tatsächlich **versenden** (heute nur `draftEmail`); kapselt `outlook.sendDraftEmail`. **Blocker** für S2/S3/S6. | **neu** |
| `data.writeToTable` | strukturierte Ergebnistabelle (PWG-Übersicht S3) als Run-Output | **neu** |
| `rbac.queryUsersByRole` | rollenbasierte Empfänger-Auflösung, **feature-instance-scoped** (`FeatureAccessRole→FeatureAccess→User`, Admin-Match `endswith("-admin")`); bei Fan-out pro iterierter Instanz | **neu** |
| Trigger-`accessControl.requiredRoles` (in `triggerExecutor`) | nur erlaubte Rolle darf manuell auslösen — **Prior Art:** Quick Actions in `mainTrustee.py` tragen bereits `requiredRoles`; Mechanik wiederverwenden | **neu (auf Basis Bestehendem)** |
| run-level `testMode` | Testlauf real, Seiteneffekt-Nodes (`email.sendEmail`/`sharepoint.upload`/`integration.pushToTarget`) unterdrücken Versand (loggen «würde senden …»); Wechselwirkung mit `input.emailWait` beachten | **neu** |
| `integration.fetchFromSource` / `integration.mapAccounts` / `integration.pushToTarget` | system-agnostischer Source→Target-Import (Quelle/Ziel = Config) | **neu** |
| `data.consolidate` (sumByKey) | Aggregation mehrerer Instanzen (Pling Holding-Konsolidierung) | **vorhanden** ✓ |
| `trigger.event` / DB-Change-Detection | ereignisgetriggerte Solutions (Notification) | **neu (Roadmap)** |
Alle **neuen** Nodes erscheinen über den Editor-Adapter (`graphicalEditor/nodeDefinitions/` + `registerNodeWithMethod`) und müssen `test_adapter_validator.py` (Drift = `frozenset()`) grün halten.
### A3 — Connectors (L1, generische API-Adapter)
- **SelectLine-Connector** (Quelle): `accountingConnectorSelectline.py`, implementiert `BaseAccountingConnector`, Auto-Discovery via `accountingRegistry.py`. Methoden: `testConnection`, `getChartOfAccounts`, `getCustomers`/`getVendors`, Lese-Seite für Belege (`getOutgoingInvoices`, Default `[]` in Base). Config: `baseUrl`, `username`, `password` (secret), optional `mandant`.
- **`TrusteeAccountingConfig.role`** (`source` | `target`) → mehrere Connector-Configs pro Instanz (eine Quelle + ein Ziel); `getActiveConfig`/`_resolveConnectorAndConfig` um `role` filtern. Optional `connectorCategory` (`accounting` | `payroll` | `invoicing`). **DB-Migration: ja.**
- **Kanonisches `Invoice`-Modell** prüfen/ergänzen (falls RMA als AR-Rechnung statt GL-Buchung).
- **Source-Pfad in `accountingBridge.py`** additiv (Push-Pfad unverändert).
- **Roadmap-Connectors** (generisch, je 1 Datei + Config): Xero (1.1.11), FileMaker (PWG 1.9.10), Kassensystem/Gastro (1.6, wartet auf Input), Lohn/Swissdec (4.2), Bank-Statement-Import (4.1.2). Je neuer Connector schaltet neue Solutions frei, ohne Architekturänderung.
### A4 — «Datenquelle = Seite» (generische Connector-UI)
- `TrusteeAccountingSettingsView` → generische **`IntegrationSettingsView`** (`category`-Prop): Verbindungen (Connector + Rolle Quelle/Ziel + Credentials + Test), Datenfluss/Import, Daten-Spiegel.
- Neue UI-Areas in `mainTrustee.py` (z. B. `ui.feature.trustee.payroll`) + Mapping in `FeatureView.tsx`**gleiche** Komponente, andere `category`.
- «Buchhaltungssystem» existiert; «Saläre»/«Fakturierung» als Folge-Kategorien (Platzhalter, nicht erste Umsetzung).
### A5 — i18n & Lokalisierung (Pflicht, regelkonform)
`ui-nyla` ist DB-backed i18n (`LanguageContext.t()`); Key-Konvention **deutscher Klartext = Key**. Regeln (`b-reference/ui-nyla/architecture.md`):
- **Statische UI-Texte** (`SolutionsView`, Katalog, Tabs, Buttons, Toasts, Empty-States, Fehlermeldungen) **müssen** `t('Deutscher Klartext')` nutzen — **nur String-Literale**, nie `t(variable)`. Status-Labels (z. B. «bestätigt»/«Abweichung») via `switch` mit Literalen, kein Map-Lookup.
- **Kundensichtbare Inhaltsdaten** = `TextMultilingual` (`{xx, en, de, …}`, `xx`=Quelltext), damit sie bereits übersetzt vom Backend kommen und direkt gerendert werden:
- **Solution-Name + Beschreibung** (Katalog/Liste/Detail).
- **Kuratierte Settings-Labels/Hilfetexte**: das auto-abgeleitete `settingsSchema` liefert technische Param-Namen — darüber liegt eine **mehrsprachige Label-Overlay-Ebene** (`TextMultilingual` pro Setting), die im Formular gerendert wird. Übersetzung via bestehendem `POST /api/i18n/translate-field`.
- **System-Template-Katalog**: Template-Titel/-Beschreibung als `TextMultilingual`.
- **Backend-Werte** (Rollen-`description`, Feature-Labels) sind bereits übersetzt → direkt rendern, **nicht** durch `t()`.
- **Akzeptanz:** kein hardcodierter deutscher String im neuen JSX; fehlende Keys erscheinen als `[key]` (sichtbar-machen).
## Teil B — Vertikale Code-Features
### B1 — `lawyer` (Mandatsvorbereitung + Dashboards) — NEU
Echtes Feature (eigene Modelle + opinionated UI), das die Plattform-Bausteine bündelt; **konsumiert** die Solution-Schicht (Matter-Prep/Dashboard-Refresh als interne Solutions/Actions).
- **Datenmodell** (`datamodelLawyer.py`): `LawyerMatter`, `LawyerPreparation`, `LawyerDashboardSnapshot`, `LawyerKpi`, `LawyerSourceMap`. **Migration: ja.**
- **Feature-Modul** `mainLawyer.py` + Template-Rollen `lawyer-admin` / `lawyer-user` / `lawyer-viewer` (feature-instance-scoped).
- **Actions** (`methodLawyer/`): `lawyer.prepareMatter`, `lawyer.refreshDashboard`, `lawyer.queryData`.
- **UI** (`ui-nyla/src/components/Lawyer/`): Dashboard-View (Tabs HR/Business/IT/Finance) + Matter-Preparation-View (opinionated, analog Workspace).
- **Connectors:** DMS (SharePoint vorhanden, iManage neu?), E-Mail (Outlook/Exchange), KYC/AML, Konflikt-Check — als generische L1-Bausteine (Teil A3-Muster).
- **Neutralisierung zwingend** (Berufsgeheimnis); Sprache UI primär Englisch (WW/Investor).
- **Demo-Asset:** synthetic Matter `M-DEMO-001` für WW-Präsentation (statisches Briefing-JSON, Backend-frei).
> Begründung Feature statt Solution: eigene Datenmodelle (`LawyerMatter`…), tiefe opinionated UI (Matter-Preparation), Domänenlogik. Dashboards/Matter-Prep dürfen intern Solutions/Actions nutzen — das Feature ist der opinionated Rahmen.
### B2 — `commcoach` (bestehend)
- Feature komplett (10+ Personas inkl. 4 PWG-Immobilien-Personas, Gamification, Seeding). **Erweiterungen:** kundenspezifische Persona-Sets als Daten (z. B. PWG-Knowledge-Set). Kein Neubau.
### B3 — `trustee` (Core, bestehend) — Erweiterungen
- Engine + Accounting-Bridge + Pipeline (`extractFromFiles`/`processDocuments`/`syncToAccounting`) + Connectors (RMA/Bexio/Abacus) bestehen.
- **Erweiterungen als Bausteine** (nicht als Kundenlogik): Firmen-/Lieferanten-Mapping-UI (1.1.4), Buchungsregeln-Engine (1.1.6), Vorsteuer-Automatik (1.1.7), Bank-Statement-Matching (4.1).
- **Exponieren statt migrieren:** Die Analyse-Templates existieren in `mainTrustee.py` **bereits als System-Workflow-Templates** (`trustee-budget-comparison`, `trustee-kpi-dashboard`, `trustee-cashflow`, `trustee-forecast`, `trustee-year-end-check`) **mit `requiredRoles`/`tags`**. Aufgabe ist nicht «aus Prompt-Templates migrieren», sondern sie als **customer-facing Solutions exponieren** (`customerFacing=true`, `settingsSchema` via `trigger.form`, mehrsprachige Labels) → solutions-plan S5. Trustee bleibt Daten-/Connector-Heimat.
### B4 — Plattform-Features (bestehend, Querschnitt)
- `neutralization` (PII-Masking, Private-LLM, Compliance-Audit) — vorhanden; pro Solution/Feature nur konfigurieren.
- Knowledge/RAG, Billing, Multi-Tenancy, Workspace — vorhanden.
- **UI/UX-Härtung** (aus altem Kundenplan Teil 2, plattformweit, nicht Solution/Feature-spezifisch): Empty-State, DE-Placeholder/i18n, Zwischen-Breakpoint 10251280px, Trust-Strip, Billing-Discovery, Connector-Onboarding-Copy. → separate UX-Charge, hier nur referenziert.
## Feature-vs-Solution — Recap (Entscheidungsmatrix)
| Kriterium | → Solution (Daten) | → Feature (Code) |
|---|---|---|
| Eigenes Datenmodell? | nein | ja (`LawyerMatter`) |
| Tiefe opinionated UI? | nein | ja (Matter-Preparation) |
| Aus Bausteinen komponierbar? | ja | nein (Domänenlogik) |
| Beispiel | SelectLine→RMA, Pling, PWG, Analysen | `lawyer`, `commcoach`, `trustee`-Core |
## Betroffene Module
- **Gateway (platform-core):**
- `features/graphicalEditor/` — neues `Solution`-Modell (`mandateId`/`workflowRef`/`runAsPrincipal`/`surfaceFeatureRef`/`settingsValues`/`instanceSelection`, A0.1) + Solution-Service + Run-Envelope-Injektion (A0.3).
- `workflows/automation2/executionEngine.py` + `scheduler/mainScheduler.py`**Run-as-Principal** statt globalem `event`-Sysadmin (A0.1); **Runtime-Mandatsvalidierung** beim `FeatureInstanceRef`-Auflösen (A0.2, `graphUtils.py`). **Migration: ja.**
- `serviceCenter/services/serviceAi`/`serviceBilling` (`recordUsage`) — **per-Node-Attribution** auf berührte `featureInstanceId`/`featureCode` statt fix `graphicalEditor` (A0.2).
- Trustee-Read-Pfade (`methodTrustee/actions/queryData.py` u. a.) — **Read-RBAC** gleichziehen mit Write-Pfad (`checkCombinedPermission`, A0.1).
- `workflows/methods/` + `graphicalEditor/nodeDefinitions/` — neue Nodes (A2): `email.sendEmail`, `data.writeToTable`, `rbac.queryUsersByRole`, `integration.*` (+ `registerNodeWithMethod`, Drift-Test).
- `workflows/.../triggerExecutor``accessControl.requiredRoles` (Prior Art: Trustee Quick-Actions) + `testMode` (A2).
- `features/trustee/accounting/connectors/accountingConnectorSelectline.py` (neu), `accountingConnectorBase.py` (Source-Methode), `accountingBridge.py` (Source-Pfad), `datamodelFeatureTrustee.py` (`role` [+ `connectorCategory`]) — **Migration: ja** (A3).
- `features/lawyer/` (neu, B1) — `mainLawyer.py`, `datamodelLawyer.py`, `methodLawyer/`, Routes. **Migration: ja.**
- **RBAC-Items**: Verwaltungs-Permission = Workflow-/Automation-Permission; Host-Feature exponiert ein RESOURCE-Item auf die Workflow-API (Muster `resource.feature.trustee.workflows.manage`) + UI-Item `ui.feature.<host>.solutions` für die Surface. DATA-AccessRules für `Solution`/Settings (`feature.graphicalEditor`-Namespace).
- **Deletion-Cascade**: `Solution`/Settings in `deleteMandate(force=True)` mit abräumen (feature-instance-scoping-Checkliste).
- `serviceCenter/services/serviceAgent/` (Toolbox `workflow`) — Use-Case→Graph (A1).
- **Frontend (ui-nyla):**
- `SolutionsView` (A1, Tab-Pattern + FormGenerator + `useConfirm`/`usePrompt`, i18n A5), `IntegrationSettingsView` (A4), `Lawyer/`-Komponenten (B1), `FeatureView.tsx`-Registry + UI-Areas. Alle Texte `t()`, Inhaltsdaten `TextMultilingual`.
- **DB-Migration:** ja (`Solution`-Modell + Settings, `TrusteeAccountingConfig.role`, Lawyer-Modelle).
- **Andere:** RBAC (Workflow-/Automation-Permission via Host-RESOURCE-Item + Principal-RBAC), Neutralisierung (selektiv via `FeatureDataSource`-Flags, feature-admin-gated), Billing (Mandate-Pool; **per-Node-Attribution** auf berührte Feature-Instanz, A0.2), i18n-Keys/Sprachsets.
## Entscheidungen
| Datum | Entscheidung | Begründung |
|-------|-------------|------------|
| 2026-06-05 | **A0.1 (korrigiert)** Ownership = **Mandant + Run-as-Principal**, nicht Host-Feature; RBAC = Principal-RBAC | Code-Befund: Workflows sind mandatsweit + identitätsgebunden, kein Host-Owner |
| 2026-06-05 | **A0.1** Scheduled Runs unter **definierter Automations-Identität** statt globalem `event`-Sysadmin | RBAC-Bypass schliessen; «Ersteller bestimmt Run-as» |
| 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 (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 |
| 2026-06-04 | Connectors = reine Adapter; `source`/`target`-Rolle als neue Achse | bidirektionales Integrations-Framework, additive Erweiterung |
| 2026-06-04 | `lawyer` bleibt **Feature** (eigene Modelle + opinionated UI), nutzt Solution-Schicht intern | Entscheidungsmatrix; Domänenlogik nicht als Graph abbildbar |
| 2026-06-04 | Trustee-Analyse-Templates als **Solutions exponieren** (existieren als System-Templates) | kundentauglich machen; Trustee bleibt Daten-/Connector-Heimat |
| 2026-06-04 | Connector-Abstraktion erst bei zweiter Kategorie (Saläre) generalisieren | YAGNI |
## Umsetzungs-Checkliste
**Teil A0 (Architektur-Leitentscheide, zuerst festzurren):**
- [ ] A0.1 `Solution`-Modell (`mandateId`/`workflowRef`/`runAsPrincipal`/`surfaceFeatureRef`/`settingsValues`) + Solution-Service
- [ ] 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 **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)
- [ ] RBAC-Items: `ui.feature.<host>.solutions`, `resource.feature.<host>.solutions.execute`, DATA-AccessRules in `TEMPLATE_ROLES`
- [ ] Output-Renderer `file`/`table`/`summary` (Spalten aus Node-Output, nicht hardcodiert)
- [ ] **`email.sendEmail`-Node** (Versand) + **`data.writeToTable`-Node** + Adapter-Drift-Test grün
- [ ] `rbac.queryUsersByRole` (feature-instance-scoped, `endswith("-admin")`), Trigger-`accessControl` (Prior Art Quick-Actions), run-level `testMode`
- [ ] Integration-Nodes `fetchFromSource`/`mapAccounts`/`pushToTarget` + Editor-Adapter
- [ ] `data.consolidate` vorhanden ✓ (kein Neubau)
- [ ] SelectLine-Connector + `TrusteeAccountingConfig.role` (+ optional `connectorCategory`) + Migration
- [ ] kanonisches `Invoice`-Modell prüfen; Source-Pfad in `accountingBridge` (additiv)
- [ ] `IntegrationSettingsView` (`category`) generalisieren
- [ ] Use-Case-Katalog (`templateScope=system` + `customerFacing`) + Workflow-Agent-Pfad
- [ ] **i18n (A5):** alle UI-Texte `t()`, Solution-Name/-Beschreibung + Settings-Labels als `TextMultilingual`
- [ ] **Deletion-Cascade:** `Solution`/Settings in `deleteMandate(force=True)`
**Teil B (Features):**
- [ ] `lawyer`: Datenmodell (5 Modelle) + `mainLawyer.py` + 3 Actions + UI (Dashboard/Matter-Prep) + RBAC + Neutralisierung + Demo-Asset
- [ ] `trustee`: Analyse-Templates → Solution-Templates; optional Mapping-UI/Buchungsregeln/Vorsteuer/Bank-Matching
- [ ] `commcoach`: kundenspezifische Persona-/Knowledge-Sets als Daten
- [ ] Roadmap-Connectors nach Bedarf (Xero, FileMaker, Lohn, Kassensystem, Bank-Import)
## Akzeptanzkriterien
| # | Kriterium (Given-When-Then) | Prio |
|---|---|---|
| 1 | Given ein neues Umsystem, When ein Connector-File + Config ergänzt wird, Then ohne Änderung an Registry/Bridge/Editor verfügbar | must |
| 2 | Given `testMode`, When eine Solution testweise läuft, Then erzeugt sie Artefakte, sendet aber keine Kommunikation | must |
| 3 | Given `rbac.queryUsersByRole`, When mit Rollen-Set + Instanz aufgerufen, Then dedupliziert die korrekten Empfänger **dieser Instanz** | must |
| 4 | Given Anwalt mit `lawyer-user`, When Matter-Preparation getriggert, Then strukturiertes Briefing in <90s, PII vor LLM neutralisiert | must |
| 5 | Given Mandate-Rolle ohne `lawyer-*`, When Lawyer-Feature geöffnet, Then Access Denied | must |
| 6 | Given Settings-Änderung, When gespeichert, Then ohne Re-Publish/Code-Deploy beim nächsten Run wirksam (Injektion, kein Graph-Schreiben) | must |
| 7 | Given ein Run mit Nodes über mehrere Trustee-Instanzen, When er läuft, Then bucht `recordUsage` je Node die **berührte** `featureInstanceId` (nicht fix `graphicalEditor`), Mandate = `mandateId` (A0.2) | must |
| 8 | Given ein Node mit Instanz-UUID aus fremdem Mandant, When der Ref aufgelöst wird, Then Laufzeit-Fehler (Mandatsgrenze, A0.2) | must |
| 9 | Given ein geplanter Run, When er ausgeführt wird, Then unter der definierten Automations-Identität (nicht globaler Sysadmin); Daten-Reads nur im RBAC-Umfang des Principals (A0.1) | must |
| 10 | Given neuer Node (`email.sendEmail`/`data.writeToTable`/…), When Adapter-Drift-Test läuft, Then `report.errors == []` | must |
## Offene Fragen
> A0.1A0.4 sind als Leitentscheide **entschieden** (oben). Offen bleiben Detail-/Externfragen:
1. **Run-as-Principal-Modell:** Ersteller-Identität vs. dedizierter Service-Principal pro Solution wie verwaltet/rotiert man die Rollen dieser Identität? Audit-Anforderungen?
2. **Per-Node-Billing-Migration:** `recordUsage`-Signatur trägt `featureInstanceId`/`featureCode` bereits reicht ein Durchreichen der Node-Ref, oder braucht es Aggregations-/Reporting-Anpassungen?
3. **`outputBinding`-Schema:** Artefakt-Referenz (Run-Output-Key vs. Document-Query); `table`-Spalten aus Node-Output-Schema ableiten Detailformat.
4. **SelectLine:** on-prem-Erreichbarkeit (VPN/Tunnel/Agent); Zielart RMA (AR vs. GL).
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:** entschieden **jetzt**, als eigener Plan `c-work/1-plan/2026-06-automation-system-component.md` (Voraussetzung für die Solution-Schicht).
## Links
- Architektur: `c-work/0-ideas/2026-06-CustomerCases-step1-architecture.md`
- Solutions: `c-work/0-ideas/2026-06-CustomerCases-step3-solutions-plan.md`
- Bausteine: `b-reference/platform-core/workflow.md`, `b-reference/platform-core/automation.md`, `b-reference/platform-core/features/trustee.md`
- RBAC/Neutralisierung: `b-reference/platform/rbac.md`, `b-reference/platform/neutralization.md`, `.cursor/rules/rbac-role-separation.mdc`
- Lawyer-Kontext (WW): `pamocreate/projects/poweron/customer-walderwyss/`
- Code: `platform-core/modules/features/trustee/accounting/` (Bridge/Base/Registry/Connectors), `features/graphicalEditor/`
## Abschluss
- [ ] Bei Annahme Build in `c-work/2-build/` (Teil A vor Teil B)
- [ ] `b-reference/` Kanon-Seiten: «Solutions / Customer Workflows», `features/lawyer.md`, Trustee «Source-Connectors»
- [ ] `TOPICS.md` + `_CHANGELOG.md` pro Phase