36 KiB
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 immerroleLabel.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) alsTextMultilingual— 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
BaseAccountingConnectorbleiben 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.pyund 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 ordnetgraphicalEditorein.
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 nachfeatureInstanceIdohne 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-
instanceSetals Ausnahme. Multi-Instanz-Verarbeitung ist die native Fähigkeit (flow.loopüber eine mandatsinterne Instanz-Auswahl, je Iteration einFeatureInstanceRef).feature-instance-scoping.mdcbraucht 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 jedesFeatureInstanceRefprüfen, dass die Instanz zummandateIddes Runs gehört und der Run-as-PrincipalFeatureAccesshat. - 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:recordUsagesoll, wenn ein Node eineFeatureInstanceRefträgt, diesefeatureInstanceId/featureCodemitschreiben (per-Node-Attribution); Mandate bleibt korrekt (mandateId). So werden Store-Kosten real dem Store, Holding-Overhead der Holding zugeordnet — über die bestehenderecordUsage-Signatur, ohne neue Billing-Achse. - RBAC der Empfänger:
rbac.queryUsersByRolelö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).settingsSchemawird daraus abgeleitet (parse_graph_defined_output_schema/{kind:"fromGraph"}existiert real). - Die
Solutionspeichert nursettingsValues(Daten). Beim Run-Start baut der Solution-Service dasrunEnvelopeaussettingsValuesund speist sie alstrigger.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(wieTrusteeAccountingConfig) und werden nur insrunEnvelopeinjiziert, nie in den Graph persistiert. - Template-Versionierung entkoppelt: Solutions referenzieren die
PUBLISHED-Version des Templates; optionalpinnedVersionIdzum Einfrieren. Bei neuemsettingsSchemawerden bestehendesettingsValuesbeim 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/AutoRunsind Orchestrierungs-Metadaten. Es ist das einzige Feature ohne DATA-Katalog. - Eigene DB
poweron_graphicaleditorhä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-runsist 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-referencesollgraphicalEditorexplizit 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-
onStartentkoppeln (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
graphicalEditorals Orchestrierungs-Substrat ein. Alles auf bestehenden Mechanismen (mandateId-Scoping,FeatureInstanceRef,runEnvelope/trigger.form,recordUsage).
A1 — Solution-Schicht (L3/L4)
Solution-Modell ingraphicalEditor(siehe A0.1):mandateId,workflowRef,runAsPrincipal,surfaceFeatureRef?(nur UI-Platzierung),settingsValues(Secrets verschlüsselt),instanceSelection?,customerFacing,outputBinding, optionalpinnedVersionId. DB-Migration: ja (Greenfield, additiv).AutoWorkflowselbst bleibt schlank; konfigurierbar übertrigger.form(A0.3) — kein Settings-Schreiben in den Graph.settingsSchemaautomatisch ableiten austrigger.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
runEnvelopeaussettingsValues, Nodes lesen via DataRef. Ausführung unterrunAsPrincipal(A0.1). Billing per-Node auf die berührteFeatureInstanceRef(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-Musterresource.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) wieTrusteeDataTablesView. Settings-Formular aussettingsSchemaviaFRONTEND_TYPE_RENDERERS/FormGenerator (Felder nie hardcodiert). Aktionen überuseConfirm()/usePrompt(). Registriert viaFeatureView.tsx+ UI-Areas inmain<Feature>.py. Alle Texte i18n (A5).- Use-Case-Katalog =
templateScope=system-Workflows mitcustomerFacing=true, gefiltert nach Host-featureCode/Tag (bestehende System-Template-Mechanik inmainGraphicalEditor._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).dashboardspä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 — nuremail.checkEmail/email.searchEmail/email.draftEmail(Senden existiert nur als Methodeoutlook.sendDraftEmail). Es gibt KEINdata.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, implementiertBaseAccountingConnector, Auto-Discovery viaaccountingRegistry.py. Methoden:testConnection,getChartOfAccounts,getCustomers/getVendors, Lese-Seite für Belege (getOutgoingInvoices, Default[]in Base). Config:baseUrl,username,password(secret), optionalmandant. TrusteeAccountingConfig.role(source|target) → mehrere Connector-Configs pro Instanz (eine Quelle + ein Ziel);getActiveConfig/_resolveConnectorAndConfigumrolefiltern. OptionalconnectorCategory(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.pyadditiv (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→ generischeIntegrationSettingsView(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 inFeatureView.tsx→ gleiche Komponente, anderecategory. - «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üssent('Deutscher Klartext')nutzen — nur String-Literale, niet(variable). Status-Labels (z. B. «bestätigt»/«Abweichung») viaswitchmit 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
settingsSchemaliefert technische Param-Namen — darüber liegt eine mehrsprachige Label-Overlay-Ebene (TextMultilingualpro Setting), die im Formular gerendert wird. Übersetzung via bestehendemPOST /api/i18n/translate-field. - System-Template-Katalog: Template-Titel/-Beschreibung als
TextMultilingual.
- Backend-Werte (Rollen-
description, Feature-Labels) sind bereits übersetzt → direkt rendern, nicht durcht(). - 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-Rollenlawyer-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-001fü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.pybereits als System-Workflow-Templates (trustee-budget-comparison,trustee-kpi-dashboard,trustee-cashflow,trustee-forecast,trustee-year-end-check) mitrequiredRoles/tags. Aufgabe ist nicht «aus Prompt-Templates migrieren», sondern sie als customer-facing Solutions exponieren (customerFacing=true,settingsSchemaviatrigger.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/— neuesSolution-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 globalemevent-Sysadmin (A0.1); Runtime-Mandatsvalidierung beimFeatureInstanceRef-Auflösen (A0.2,graphUtils.py). Migration: ja.serviceCenter/services/serviceAi/serviceBilling(recordUsage) — per-Node-Attribution auf berührtefeatureInstanceId/featureCodestatt fixgraphicalEditor(A0.2).- Trustee-Read-Pfade (
methodTrustee/actions/queryData.pyu. 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-Itemui.feature.<host>.solutionsfür die Surface. DATA-AccessRules fürSolution/Settings (feature.graphicalEditor-Namespace). - Deletion-Cascade:
Solution/Settings indeleteMandate(force=True)mit abräumen (feature-instance-scoping-Checkliste). serviceCenter/services/serviceAgent/(Toolboxworkflow) — 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 Textet(), InhaltsdatenTextMultilingual.
- 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 (recordUsagemit berührter Instanz) + Klarstellung infeature-instance-scoping.mdc - A0.3 Run-Envelope-Injektion aus
settingsValues(kein Graph-Schreiben) +settingsSchema-Drift-Validierung + optionalpinnedVersionId - A0.4
b-reference:graphicalEditorals 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 inTEMPLATE_ROLES - Output-Renderer
file/table/summary(Spalten aus Node-Output, nicht hardcodiert) email.sendEmail-Node (Versand) +data.writeToTable-Node + Adapter-Drift-Test grünrbac.queryUsersByRole(feature-instance-scoped,endswith("-admin")), Trigger-accessControl(Prior Art Quick-Actions), run-leveltestMode- Integration-Nodes
fetchFromSource/mapAccounts/pushToTarget+ Editor-Adapter data.consolidatevorhanden ✓ (kein Neubau)- SelectLine-Connector +
TrusteeAccountingConfig.role(+ optionalconnectorCategory) + Migration - kanonisches
Invoice-Modell prüfen; Source-Pfad inaccountingBridge(additiv) IntegrationSettingsView(category) generalisieren- Use-Case-Katalog (
templateScope=system+customerFacing) + Workflow-Agent-Pfad - i18n (A5): alle UI-Texte
t(), Solution-Name/-Beschreibung + Settings-Labels alsTextMultilingual - Deletion-Cascade:
Solution/Settings indeleteMandate(force=True)
Teil B (Features):
lawyer: Datenmodell (5 Modelle) +mainLawyer.py+ 3 Actions + UI (Dashboard/Matter-Prep) + RBAC + Neutralisierung + Demo-Assettrustee: Analyse-Templates → Solution-Templates; optional Mapping-UI/Buchungsregeln/Vorsteuer/Bank-Matchingcommcoach: 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:
- Run-as-Principal-Modell: Ersteller-Identität vs. dedizierter Service-Principal pro Solution — wie verwaltet/rotiert man die Rollen dieser Identität? Audit-Anforderungen?
- Per-Node-Billing-Migration:
recordUsage-Signatur trägtfeatureInstanceId/featureCodebereits — reicht ein Durchreichen der Node-Ref, oder braucht es Aggregations-/Reporting-Anpassungen? outputBinding-Schema: Artefakt-Referenz (Run-Output-Key vs. Document-Query);table-Spalten aus Node-Output-Schema ableiten — Detailformat.- SelectLine: on-prem-Erreichbarkeit (VPN/Tunnel/Agent); Zielart RMA (AR vs. GL).
- Lawyer-Connectors: iManage/KYC/Konflikt-Check-APIs — welche mandantenspezifisch, welche generisch?
- Connector-Generalisierung: wann Capability-Mixins (
AccountingCapable/PayrollCapable) ziehen? email.sendEmail-Scope: eigener Node vs.email.draftEmail+ Auto-Send-Flag — und Verhältnis zuinput.emailWait/testMode.graphicalEditor-Entkopplung: Scheduler-Boot vom Feature-onStartlösen — jetzt oder als spätere Roadmap (A0.4)?
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.mdpro Phase