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

36 KiB
Raw Blame History

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 (pragmatisch, kein Big-Bang):

  • 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).

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.tsxgleiche 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.pyRun-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/.../triggerExecutoraccessControl.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 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 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 b-reference: graphicalEditor als Orchestrierungs-Substrat dokumentieren (Roadmap: Scheduler-Boot entkoppeln — später)

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: Scheduler-Boot vom Feature-onStart lösen — jetzt oder als spätere Roadmap (A0.4)?
  • 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