# Plan: Features & Plattform-Bausteine — der Code hinter den Solutions > **Baut auf:** `2026-06-CustomerCases-step1-architecture.md` (Schichten L1–L4, 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.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 1025–1280px, 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..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..solutions`, `resource.feature..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.1–A0.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