diff --git a/c-work/0-ideas/2026-06-CustomerCases-step1-architecture.md b/c-work/0-ideas/2026-06-CustomerCases-step1-architecture.md index d178977..79a40f7 100644 --- a/c-work/0-ideas/2026-06-CustomerCases-step1-architecture.md +++ b/c-work/0-ideas/2026-06-CustomerCases-step1-architecture.md @@ -92,8 +92,8 @@ Beispiele auf dieselbe Form gebracht: | Bedürfnis | Trigger | Bausteine (Nodes) | Settings | Output | |---|---|---|---|---| | 2 Systeme synchronisieren | `trigger.schedule` 06:00 | source-connector → `mapAccounts` → target-connector | Quelle/Ziel, Mapping | Sync-Log/Status | -| **Monatsreporting 5 Buchhaltungen (Pling)** | `trigger.schedule` 15./06:00 + `trigger.manual` (rollen-beschränkt) | `loop`(5 Instanzen) → `refreshAccountingData` → `queryData`(Salden akt.+VJ) → `data.consolidate` → `ai.generateDocument`×6 → `rbac.queryUsersByRole` → `outlook.send` → `file.create` | Perioden, Konten-Ranges, Empfänger-Rollen, Cron, Branding | 6 PDFs + Mails + Lauf-Protokoll | -| Mietzinsbestätigungen (PWG) | `trigger.schedule` | `sharepoint.listFiles` → `loop` → `extractFromFiles` → `ai.prompt` → `data.write` → `email.send` | Ordner, Abacus-Mandant, Empfänger | Übersichtstabelle + Mail | +| **Monatsreporting 5 Buchhaltungen (Pling)** | `trigger.schedule` 15./06:00 + `trigger.manual` (rollen-beschränkt) | `loop`(Instanz-Auswahl, mandatsintern) → `refreshAccountingData` → `queryData`(Salden akt.+VJ) → `data.consolidate` → `ai.generateDocument`×6 → `rbac.queryUsersByRole` → `email.sendEmail` → `file.create` | Perioden, Konten-Ranges, Empfänger-Rollen, Cron, Branding | 6 PDFs + Mails + Lauf-Protokoll | +| Mietzinsbestätigungen (PWG) | `trigger.schedule` | `sharepoint.listFiles` → `loop` → `extractFromFiles` → `ai.prompt` → `data.writeToTable` → `email.sendEmail` | Ordner, Abacus-Mandant, Empfänger | Übersichtstabelle + Mail | | Matter Preparation (Lawyer) | `trigger.manual/form` | `lawyer.prepareMatter` (Agent über Quellen) | Lookback, Quellen | Briefing + Dashboards | → **90 % des Kundenwerts ist Komposition, nicht Code.** Die Bausteine sind da. Die **Verpackung + Verwaltung** fehlt. @@ -204,7 +204,7 @@ bereit und lässt ihn einen Graphen erzeugen → als Draft-Version speichern → ## Betroffene Module (Skizze) - **Gateway:** - - `features/graphicalEditor/` — `AutoWorkflow` um Settings-Schema + Presentation-Binding + `customerFacing`-Flag erweitern; Settings-Record-Modell. **DB-Migration: ja (Greenfield, additiv).** + - `features/graphicalEditor/` — dünnes `Solution`-Modell (`mandateId`/`workflowRef`/`runAsPrincipal`/`surfaceFeatureRef`/`settingsValues`/`instanceSelection`/`outputBinding`/`customerFacing`) + Solution-Service; `AutoWorkflow` selbst bleibt schlank (Konfiguration via `trigger.form`). **DB-Migration: ja (Greenfield, additiv).** Details: step3-features-plan A0/A1. - `workflows/scheduler/mainScheduler.py` — unverändert nutzbar (Trigger-Policy mappt auf bestehende Schedule-Logik). - `interfaces/interfaceBootstrap.py` — Use-Case-Katalog als System-Templates. - `serviceCenter/services/serviceAgent/` (Toolbox `workflow`) — Use-Case→Graph-Generierung; ggf. Prompt/Guardrails ergänzen. @@ -239,8 +239,8 @@ bereit und lässt ihn einen Graphen erzeugen → als Draft-Version speichern → ## Beantwortete Fragen (Entscheid 2026-06-04) -1. **Daten-Heimat:** ✅ **`AutoWorkflow` erweitern** (kein eigenes Solution-Modell). - *UI-Konsequenz (deine Frage):* Die Backend-Erweiterung berührt die technische Automation-UI (Graphical Editor / Ops) **nicht** — es entstehen **nicht** «viele Automation-Seiten». Stattdessen **eine** neue kundentaugliche Seite **«Lösungen» pro Feature**, die denselben `AutoWorkflow` nur anders *präsentiert*. Aufbau: **Liste** (aktiv/inaktiv, letzter/nächster Lauf) · **Katalog/Neu** (Vorlage oder AI) · **Detail mit Tabs** (*Einstellungen · Testlauf · Läufe · Ausgabe*). Also: **eine Seite pro Feature, Unteransichten + Tabs im Detail** — siehe Mockup. +1. **Daten-Heimat:** ✅ **Dünnes `Solution`-Modell in `graphicalEditor`**, das einen `AutoWorkflow` (Orchestrierungs-Substrat) + `mandateId` (Owner-Achse Tenant) + `runAsPrincipal` (Owner-Achse Identität) + `settingsValues` bindet. *Code-verifiziert korrigiert in step3-features-plan A0.1/A0.2/A0.4:* Workflows sind **mandatsweit + identitätsgebunden** über beliebige Feature-Instanzen — es gibt **keinen** Host-Feature-Owner; «im Trustee angezeigt» ist nur Präsentation. RBAC = Principal-RBAC; Billing per-Node auf die berührte Feature-Instanz. Settings werden zur Laufzeit injiziert (A0.3), nie in den Graph geschrieben. + *UI-Konsequenz:* Die technische Automation-UI (Graphical Editor / Ops) wird **nicht** berührt — es entstehen **nicht** «viele Automation-Seiten». Stattdessen **eine** kundentaugliche Seite **«Lösungen»**, als Surface pro Feature eingebettet. Aufbau: **Liste** (aktiv/inaktiv, letzter/nächster Lauf) · **Katalog/Neu** (Vorlage oder AI) · **Detail mit Tabs** (*Einstellungen · Testlauf · Läufe · Ausgabe*) — siehe Mockup. 2. **Settings-Schema-Quelle:** ✅ **Automatisch abgeleitet** aus `trigger.form` + als «exposed» markierten Node-Parametern. (Kuratierte Overrides bei Bedarf später.) 3. **AI-Generierung:** ✅ **Ja.** Katalog-Templates deterministisch; AI nur für Neues, mit Review-/Freigabe-Schritt vor Aktivierung. 4. **Output/Dashboard:** ⚠️ **Richtung entschieden, Detail offen — Empfehlung im nächsten Abschnitt.** @@ -271,7 +271,7 @@ Der schwierigste Punkt, weil «Output» von «PDF im Mail-Anhang» bis «interak - **Die wichtigste Disziplin ist die Feature-vs-Solution-Grenze.** Ohne sie bauen wir wieder pro Kunde ein Feature (siehe die Klassifikationstabelle im `CustomerCases-step3-solutions-plan`). Mit ihr wird der Default «konfigurieren statt coden». - **Settings strukturieren, nicht in Notes.** Das ist die kleine Entscheidung mit grosser Wirkung: sie macht Solutions wartbar, sicher und versionierbar. - **Pling als idealer erster realer Beleg.** Der Fall ist bereits voll spezifiziert (~2 Wochen Build) und braucht nur zwei *generische* Ergänzungen (`rbac.queryUsersByRole`, Trigger-`accessControl`). Er beweist die These am konkreten Geld-Use-Case und liefert nebenbei wiederverwendbare Bausteine. -- **Inkrementeller erster Schnitt:** (1) `AutoWorkflow` um Settings-Schema + `customerFacing` + Instanz-Set erweitern; (2) 1–2 kuratierte System-Templates (z. B. «Systeme synchronisieren» und «Periodisches Reporting» aus Pling); (3) minimale `SolutionsView` (Liste + Settings + Test + Aktivieren + Run-Historie) im Trustee. Damit ist das Muster end-to-end belegt — demonstrierbar am SelectLine→RMA- **und** am Pling-Fall. +- **Inkrementeller erster Schnitt:** (1) `Solution`-Modell + Settings-Injektion (step3-features-plan A0) anlegen; (2) 1–2 kuratierte System-Templates (z. B. «Systeme synchronisieren» und «Periodisches Reporting» aus Pling); (3) minimale `SolutionsView` (Liste + Settings + Test + Aktivieren + Run-Historie) im Trustee. Damit ist das Muster end-to-end belegt — demonstrierbar am SelectLine→RMA- **und** am Pling-Fall. - **Verhältnis zu den anderen Plänen:** Der Umsystem-/Datenquellen-Plan liefert **L1-Connectors**; dieser Plan liefert **L3/L4**. Lawyer ist ein **Feature**, das L1–L4 konsumiert. Drei Pläne, eine Architektur. ## Links diff --git a/c-work/0-ideas/2026-06-CustomerCases-step2-communication-mockup.html b/c-work/0-ideas/2026-06-CustomerCases-step2-communication-mockup.html index 08bf59b..eef3f5b 100644 --- a/c-work/0-ideas/2026-06-CustomerCases-step2-communication-mockup.html +++ b/c-work/0-ideas/2026-06-CustomerCases-step2-communication-mockup.html @@ -545,7 +545,7 @@ ["ok", "ai.generateDocument · Holding", "1 PDF, neutralisiert", "31s"], ["ok", "ai.generateDocument · Store-KPI ×5", "5 PDFs", "26s"], ["ok", "rbac.queryUsersByRole", "8 Empfänger aufgelöst", "0.4s"], - ["ok", "outlook.send", "8 Mails versendet", "3s"] + ["ok", "email.sendEmail", "8 Mails versendet", "3s"] ], files: [ ["Monatsbericht Holding Mai 2026", "PDF · 412 KB · Holding-Instanz", "PDF"], diff --git a/c-work/0-ideas/2026-06-CustomerCases-step2-communication-product-summary.md b/c-work/0-ideas/2026-06-CustomerCases-step2-communication-product-summary.md index 7b6ef58..9257e65 100644 --- a/c-work/0-ideas/2026-06-CustomerCases-step2-communication-product-summary.md +++ b/c-work/0-ideas/2026-06-CustomerCases-step2-communication-product-summary.md @@ -74,9 +74,9 @@ Settings-Formular wird aus dem `settingsSchema` gerendert (FrontendType). «Im E ## Bauen — erster Schnitt -1. `AutoWorkflow` um `settingsSchema` + `customerFacing` + Instanz-Set erweitern (+ Settings-Record, Migration). +1. `Solution`-Modell (Workflow + Mandant + Run-as-Principal + Settings) anlegen; Settings werden zur Laufzeit injiziert, nicht in den Graph geschrieben (Migration). 2. 2 System-Templates: «Systeme synchronisieren» (SelectLine→RMA) + «Periodisches Reporting» (Pling). 3. Generische `SolutionsView` im Trustee: Liste + Settings + Testlauf + Aktivieren + Läufe. -4. Generische Bausteine: `rbac.queryUsersByRole`, Trigger-`accessControl`, run-level `testMode` (Kommunikation im Testlauf unterdrücken). +4. Generische Bausteine: `rbac.queryUsersByRole`, `email.sendEmail` (Versand-Node), Trigger-`accessControl`, run-level `testMode` (Kommunikation im Testlauf unterdrücken). → End-to-end belegt an **SelectLine→RMA** und **Pling**. diff --git a/c-work/0-ideas/2026-06-CustomerCases-step3-features-plan.md b/c-work/0-ideas/2026-06-CustomerCases-step3-features-plan.md index ed81a14..1e88975 100644 --- a/c-work/0-ideas/2026-06-CustomerCases-step3-features-plan.md +++ b/c-work/0-ideas/2026-06-CustomerCases-step3-features-plan.md @@ -23,38 +23,116 @@ Dieser Plan bündelt allen **Code**, der zu bauen ist, in zwei Teilen: ## 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 von `AutoWorkflow`** + Settings-Record, keine Parallel-Welt → Scheduler/Runs/Versioning unverändert. +- **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`). +- **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) -- **`AutoWorkflow` erweitern** um `settingsSchema`, `outputBinding`, `customerFacing`-Flag, **Instanz-Set** (Multi-Instanz-Fan-out). **DB-Migration: ja (Greenfield, additiv).** -- **Settings-Record-Modell** (`settingsValues` pro Solution/Instanz; Secrets verschlüsselt wie Connector-Configs). -- **`settingsSchema` automatisch ableiten** aus `trigger.form` + als «exposed» markierten Node-Params (kuratierte Overrides später). -- **Solution-Routes** (dünn über bestehende Editor-/Run-Routes): CRUD Settings, Test-Run, Activate/Deactivate. -- **`SolutionsView`** (ui-nyla): generische «Lösungen»-Seite pro Feature — Liste · Katalog/Neu · Detail mit Tabs (Einstellungen · Testlauf · Läufe · Ausgabe). Settings-Formular aus `settingsSchema` (FrontendType-getrieben). Registriert wie andere Views (`FeatureView.tsx` + UI-Areas in `main.py`). -- **Use-Case-Katalog** als System-Templates (`interfaces/interfaceBootstrap.py`). +- **`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), `summary` (`AutoRun`/`AutoStepLog`). `dashboard` später (AI-Report/Canvas-Pipeline). +- **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 | |---|---|---| -| `rbac.queryUsersByRole` | rollenbasierte Empfänger-Auflösung (Verteiler aus Feature-Rollen statt Listen) | **neu** | -| Trigger-`accessControl.requiredRoles` (in `triggerExecutor`) | nur erlaubte Rolle darf manuell auslösen | **neu** | -| run-level `testMode` | Testlauf real, Kommunikations-/Seiteneffekt-Nodes (Mail/Outlook/Teams/Webhook) unterdrücken Versand (loggen «würde senden …») | **neu** | +| `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) | prüfen/ggf. neu | -| `data.writeToTable` / Tabellen-Output | Ergebnistabellen (PWG-Übersicht) | prüfen/ggf. neu | +| `data.consolidate` (sumByKey) | Aggregation mehrerer Instanzen (Pling Holding-Konsolidierung) | **vorhanden** ✓ | | `trigger.event` / DB-Change-Detection | ereignisgetriggerte Solutions (Notification) | **neu (Roadmap)** | -Alle erscheinen über den Editor-Adapter (`graphicalEditor/nodeDefinitions/`) als Bausteine. +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) @@ -70,6 +148,18 @@ Alle erscheinen über den Editor-Adapter (`graphicalEditor/nodeDefinitions/`) al - 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 @@ -94,7 +184,7 @@ Echtes Feature (eigene Modelle + opinionated UI), das die Plattform-Bausteine b - 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). -- **Migration:** Analyse-Prompt-Templates (Budget/KPI/Cashflow/Prognose/Jahresabschluss) aus `mainTrustee.py` → kuratierte **Solution-Templates** (→ solutions-plan S5). Trustee bleibt Daten-/Connector-Heimat, die Analysen werden Solutions. +- **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) @@ -114,41 +204,63 @@ Echtes Feature (eigene Modelle + opinionated UI), das die Plattform-Bausteine b ## Betroffene Module - **Gateway (platform-core):** - - `features/graphicalEditor/` — `AutoWorkflow`-Erweiterung + Settings-Record + Solution-Routes (A1). **Migration: ja.** - - `workflows/methods/` + `graphicalEditor/nodeDefinitions/` — generische Nodes (A2). - - `workflows/.../triggerExecutor` — `accessControl.requiredRoles` + `testMode` (A2). + - `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.** - - `interfaces/interfaceBootstrap.py` — Use-Case-Katalog/System-Templates (A1). + - **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), `IntegrationSettingsView` (A4), `Lawyer/`-Komponenten (B1), `FeatureView.tsx`-Registry + UI-Areas. -- **DB-Migration:** ja (Solution-Schicht, `TrusteeAccountingConfig.role`, Lawyer-Modelle). -- **Andere:** RBAC (neue UI-Areas, bestehende Feature-Rollen), Neutralisierung, Billing (Run-/Sync-Volumen). + - `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-Prompt-Templates → **Solution-Templates** migrieren | kundentauglich machen; Trustee bleibt Daten-/Connector-Heimat | +| 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 A (Enabler, zuerst):** -- [ ] `AutoWorkflow`-Erweiterung (`settingsSchema`/`outputBinding`/`customerFacing`/Instanz-Set) + Settings-Record + Migration -- [ ] Solution-Routes (CRUD/Test/Activate) + `SolutionsView` + Settings-Formular-Renderer -- [ ] Output-Renderer `file`/`table`/`summary` -- [ ] `rbac.queryUsersByRole`, Trigger-`accessControl`, run-level `testMode` +**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..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` / `data.writeToTable` prüfen/ergänzen +- [ ] `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/System-Templates + Workflow-Agent-Pfad +- [ ] 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 @@ -162,18 +274,27 @@ Echtes Feature (eigene Modelle + opinionated UI), das die Plattform-Bausteine b |---|---|---| | 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 aufgerufen, Then dedupliziert die korrekten Empfänger (Pling-Verteiler) | 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 Solution-Schicht, When ein Kunde Settings ändert, Then ohne Code-Deploy wirksam | 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 -1. **Solution-Daten-Heimat:** `AutoWorkflow`-Erweiterung (empfohlen) — Settings-Record als eigenes Modell oder JSON-Feld? -2. **`outputBinding`-Schema:** Artefakt-Referenz (Run-Output-Key vs. Document-Query); `table`-Spalten aus Node-Output ableiten? -3. **SelectLine:** on-prem-Erreichbarkeit (VPN/Tunnel/Agent); Zielart RMA (AR vs. GL). -4. **Lawyer-Connectors:** iManage/KYC/Konflikt-Check-APIs — welche mandantenspezifisch, welche generisch? -5. **Connector-Generalisierung:** wann Capability-Mixins (`AccountingCapable`/`PayrollCapable`) ziehen? +> 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:** Scheduler-Boot vom Feature-`onStart` lösen — jetzt oder als spätere Roadmap (A0.4)? ## Links diff --git a/c-work/0-ideas/2026-06-CustomerCases-step3-solutions-plan.md b/c-work/0-ideas/2026-06-CustomerCases-step3-solutions-plan.md index 843d7a4..f090b18 100644 --- a/c-work/0-ideas/2026-06-CustomerCases-step3-solutions-plan.md +++ b/c-work/0-ideas/2026-06-CustomerCases-step3-solutions-plan.md @@ -19,11 +19,12 @@ Business-Treiber: jeder Use Case wird zu Konfiguration statt Engineering → Tim ## Fokus und kritische Details -- **Solution = Daten, nie Code.** Konten-Mappings, Empfänger, Perioden, Ordnerpfade, Cron-Zeiten, Instanz-Auswahl = **Settings**. Tauchen Kundennamen oder `if kunde == X` im Code auf, ist die Solution falsch geschnitten. -- **Multi-Instanz-Fan-out.** Eine Solution kann über ein **Set von Feature-Instanzen** laufen (Pling: 5 Stores). Das Settings-Modell trägt eine Instanz-Auswahl. -- **Voraussetzung sind generische Bausteine, keine kundenspezifischen.** Wo ein Baustein fehlt (z. B. `rbac.queryUsersByRole`, ein SelectLine-Connector), wird er **generisch** im features-plan gebaut und hier nur referenziert. -- **Neutralisierung** für jeden LLM-Pfad (Belege/Berichte mit Personendaten) — über das bestehende Gate, selektiv (Namen/Beschreibungen pseudonymisiert, Zahlen/Konten lesbar). -- **Testlauf real, ohne Versand:** Solutions laufen im Test echt, nur Kommunikation wird via run-level `testMode` unterdrückt. +- **Solution = Daten, nie Code.** Konten-Mappings, Empfänger, Perioden, Ordnerpfade, Cron-Zeiten, Instanz-Auswahl = **Settings**. Tauchen Kundennamen oder `if kunde == X` im Code auf, ist die Solution falsch geschnitten. Settings werden zur Laufzeit injiziert (features-plan A0.3), nie in den Graph geschrieben. +- **Owner-Achse = Mandant + Run-as-Principal** (features-plan A0.1): eine Solution gehört dem **Mandanten** und läuft **im Namen eines Principals** (interaktiv: User; geplant: definierte Automations-Identität). «Im Trustee angezeigt» ist nur UI-Platzierung. Datenzugriff richtet sich nach der **RBAC des Principals**, über Feature-Grenzen hinweg. +- **Multi-Feature/Multi-Instanz ist nativ** (features-plan A0.2): per-Node `FeatureInstanceRef`; eine Solution kann mandatsintern über **mehrere Feature-Instanzen** (Pling: 5 Trustee-Instanzen einer Holding) und sogar **mehrere Features** laufen. Echte Schranke = der **Mandant** (kein Cross-Mandate). Kosten werden **per-Node** der berührten Instanz zugeordnet. +- **Voraussetzung sind generische Bausteine, keine kundenspezifischen.** Wo ein Baustein fehlt (z. B. `rbac.queryUsersByRole`, `email.sendEmail`, `data.writeToTable`, ein SelectLine-Connector), wird er **generisch** im features-plan gebaut und hier nur referenziert. +- **Neutralisierung** für jeden LLM-Pfad (Belege/Berichte mit Personendaten) — selektiv über `FeatureDataSource`-Flags (`neutralizeFields`), **feature-admin-gated** konfiguriert (Namen/Beschreibungen pseudonymisiert, Zahlen/Konten lesbar). +- **Testlauf real, ohne Versand:** Solutions laufen im Test echt, nur Seiteneffekt-Nodes (`email.sendEmail`/`sharepoint.upload`/`integration.pushToTarget`) unterdrücken via run-level `testMode`. ## Ziel und Nicht-Ziele @@ -74,12 +75,13 @@ Zwei Kunden mit unterschiedlichem Kontenplan-Mapping = **zwei Solutions/Configs, |---|---| | **Bedürfnis** | Am 15./Monat aus 5 RMA-Buchhaltungen konsolidieren, 6 PDFs erzeugen, rollenbasiert mailen | | **Trigger** | `trigger.schedule` `0 6 15 * *` (Europe/Zurich) + `trigger.manual` (rollen-beschränkt) | -| **Bausteine (Graph)** | `flow.loop`(5 Instanzen) → `trustee.refreshAccountingData` → `trustee.queryData` (Salden akt.+Vorjahr) → `data.consolidate` (sumByKey) → `ai.generateDocument`×6 (1 Holding + 5 Store) → `rbac.queryUsersByRole` → `outlook.send` → `file.create` (Archiv) | -| **Settings (L3)** | **Instanz-Set** (5 Stores), Konten-Ranges (Umsatz 3000–3999, Warenkosten 6000–6299, Personal 5000–5099), Cron, Empfänger-**Rollen** (Holding→`trustee-accountant`/`trustee-viewer`, Store→`trustee-client`, Protokoll→`trustee-admin`), `stopOnIncomplete` | +| **Bausteine (Graph)** | `flow.loop`(Instanz-Auswahl, mandatsintern) → `trustee.refreshAccountingData` → `trustee.queryData` (Salden akt.+Vorjahr) → `data.consolidate` (sumByKey) → `ai.generateDocument`×6 (1 Holding + 5 Store) → `rbac.queryUsersByRole` (pro Instanz) → `email.sendEmail` → `file.create` (Archiv) | +| **Settings (L3)** | **Instanz-Auswahl** (5 Trustee-Instanzen *derselben Holding/Mandant* — bestätigt), Konten-Ranges (Umsatz 3000–3999, Warenkosten 6000–6299, Personal 5000–5099), Cron, Empfänger-**Rollen** (Holding→`trustee-accountant`/`trustee-viewer`, Store→`trustee-client`, Protokoll→`trustee-admin` — alle verifiziert vorhanden), `stopOnIncomplete` | +| **Owner/Billing** | Owner = Mandant; Run-as-Principal = definierte Automations-Identität (geplanter Lauf). **Per-Node-Billing**: jede Store-`refresh/query` bucht die berührte Store-Instanz, Konsolidierung/Report/Versand die Holding-Instanz (A0.1/A0.2) | | **Output (L4)** | `file` — 6 PDFs, archiviert als `TrusteeDocument`; + `summary` Lauf-Protokoll | -| **Sonderfall** | Buchhaltung am 15. nicht abgeschlossen → durchlaufen mit Markierung (Conditional nach refresh), steuerbar via `stopOnIncomplete` | -| **Neutralisierung** | selektiv (Namen/Beschreibungen ja; Zahlen/Konten nein) | -| **Voraussetzungen (features-plan)** | `rbac.queryUsersByRole`, Trigger-`accessControl.requiredRoles`, `data.consolidate` (prüfen), `testMode` | +| **Sonderfall** | Buchhaltung am 15. nicht abgeschlossen → durchlaufen mit Markierung (`flow.ifElse` nach refresh), steuerbar via `stopOnIncomplete` | +| **Neutralisierung** | selektiv via `FeatureDataSource`-Flags (Namen/Beschreibungen ja; Zahlen/Konten nein) | +| **Voraussetzungen (features-plan)** | `rbac.queryUsersByRole`, **`email.sendEmail`-Node (neu, Blocker)**, Trigger-`accessControl.requiredRoles`, `testMode`; `data.consolidate` vorhanden ✓ | Vollständig spezifiziert (High-Level + Anhang); ~2 Wochen Build → bester End-to-End-Beleg der Solution-Schicht. @@ -89,11 +91,11 @@ Vollständig spezifiziert (High-Level + Anhang); ~2 Wochen Build → bester End- |---|---| | **Bedürfnis** | Gescannte Rücklauf-Bestätigungen (≈3'200/Jahr) prüfen, Status klassifizieren, Antwortvorschläge | | **Trigger** | `trigger.schedule` (täglich) oder `trigger.manual` | -| **Bausteine (Graph)** | `sharepoint.listFiles` → `flow.loop` → `sharepoint.downloadFile` + `trustee.extractFromFiles` (OCR) → `ai.prompt` (Scan vs. Abacus-Originaldaten, Status, Antwortvorschlag) → `data.writeToTable` → `email.send` | -| **Settings (L3)** | Scan-Ordner (SharePoint), Abacus-Mandant + Mietzins-Stammdaten-Abfrage, Empfänger (Sachbearbeiter), Prompt-Variante | +| **Bausteine (Graph)** | `sharepoint.listFiles` → `flow.loop` → `sharepoint.readFile` + `trustee.extractFromFiles` (OCR) → `ai.prompt` (Scan vs. Abacus-Originaldaten, Status, Antwortvorschlag) → **`input.humanTask`** (Review der Antwortvorschläge vor Versand, optional) → `data.writeToTable` → `email.sendEmail` | +| **Settings (L3)** | Scan-Ordner (SharePoint), Abacus-Mandant + Mietzins-Stammdaten-Abfrage, Empfänger (Sachbearbeiter), Prompt-Variante, Review-Pflicht ja/nein | | **Output (L4)** | `table` — Übersicht verarbeiteter Bestätigungen (bestätigt / Abweichung / fehlende Unterschrift / unleserlich) + Mail-Zusammenfassung | | **Kunden** | PWG (Pilot, Versand Sommer 2026, Deadline-gebunden) | -| **Voraussetzungen (features-plan)** | Prompt-Template «Mietzinsbestätigung prüfen», Abacus-Stammdaten-Abfrage konfigurieren, `data.writeToTable`-Output-Renderer | +| **Voraussetzungen (features-plan)** | Prompt-Template «Mietzinsbestätigung prüfen», Abacus-Stammdaten-Abfrage konfigurieren, **`data.writeToTable`-Node (neu)**, **`email.sendEmail`-Node (neu)**; `sharepoint.readFile`/`input.humanTask` vorhanden ✓ | ### S4 — «Beleg-Import» (periodische Spesen-/Belegverarbeitung; 1.1, 1.10.5) @@ -117,7 +119,7 @@ Vollständig spezifiziert (High-Level + Anhang); ~2 Wochen Build → bester End- | **Settings (L3)** | Periode(n), Vergleichsbasis (Vorjahr/Budget), optionaler Budget-Upload, KPI-Auswahl, Empfänger | | **Output (L4)** | `table` (KPIs), `file` (Report-PDF) oder `summary` | | **Kunden** | Bling, Quid, allgemein Treuhand | -| **Voraussetzungen** | bestehende Analyse-Prompt-Templates als Solution-Templates verfügbar machen (Migration aus `mainTrustee.py` → kuratierte Templates) | +| **Voraussetzungen** | **bereits vorhandene** System-Templates (`trustee-budget-comparison`/`-kpi-dashboard`/`-cashflow`/`-forecast`/`-year-end-check`) nur als `customerFacing` Solutions exponieren (kein Neubau) — features-plan B3 | ### S6 — «Benachrichtigung / Report-Verteilung» (1.9.12, 4.5.8) @@ -125,18 +127,18 @@ Vollständig spezifiziert (High-Level + Anhang); ~2 Wochen Build → bester End- |---|---| | **Bedürfnis** | Bei Ereignis/Zeitplan Bericht erzeugen und rollenbasiert verteilen; KPI-Alerts | | **Trigger** | `trigger.schedule` / `trigger.event` (DB-Change → features-plan) | -| **Bausteine (Graph)** | Daten lesen → `ai.generateDocument` (optional) → `rbac.queryUsersByRole` → `email.send`/`outlook.send` | +| **Bausteine (Graph)** | Daten lesen → `ai.generateDocument` (optional) → `rbac.queryUsersByRole` → `email.sendEmail` | | **Settings (L3)** | Auslöser/Schwellwerte, Empfänger-Rollen, Vorlage | | **Output (L4)** | `summary` + Mail | -| **Voraussetzungen (features-plan)** | `rbac.queryUsersByRole`, optional `trigger.event`/DB-Change-Detection | +| **Voraussetzungen (features-plan)** | `rbac.queryUsersByRole`, **`email.sendEmail`-Node (neu)**, optional `trigger.event`/DB-Change-Detection | ## Gemeinsames Settings-Muster (alle Solutions) -- **`settingsSchema`** automatisch aus `trigger.form` + exposed Node-Params abgeleitet (kuratierte Overrides später). -- **`settingsValues`** inkl. **Instanz-Set** (Multi-Instanz-Fan-out). +- **`settingsSchema`** automatisch aus `trigger.form` + exposed Node-Params abgeleitet; kundensichtbare Labels als `TextMultilingual` (i18n, features-plan A5). +- **`settingsValues`** (Daten, Secrets verschlüsselt) inkl. **Instanz-Auswahl** (mandatsintern, mehrere Feature-Instanzen/Features möglich, A0.2); zur Laufzeit als `runEnvelope` injiziert (A0.3) — **kein** Graph-Schreiben. - **`triggerPolicy`** manuell/Zeitplan/Event/Form + `accessControl.requiredRoles` (bestehende Feature-Rollen). - **`outputBinding.kind`** = `file | table | summary` (Dashboard später). -- **Lebenszyklus** in der «Lösungen»-Seite: Liste · Katalog/Neu (Vorlage oder AI) · Detail mit Tabs (Einstellungen · Testlauf · Läufe · Ausgabe). +- **Lebenszyklus** in der «Lösungen»-Seite (UX-Muster wie `TrusteeDataTablesView`: Tabs + `?tab=`/`?solutionId=`-Deep-Links, FormGenerator, `useConfirm`/`usePrompt`, alle Texte `t()`): Liste · Katalog/Neu (Vorlage oder AI) · Detail mit Tabs (Einstellungen · Testlauf · Läufe · Ausgabe). ## Betroffene Module @@ -173,15 +175,18 @@ Vollständig spezifiziert (High-Level + Anhang); ~2 Wochen Build → bester End- | 2 | Given konfigurierte S2 (Pling), When der Monatslauf läuft, Then 6 PDFs erzeugt + rollenbasiert versendet + archiviert; manueller Start nur für `trustee-admin` | must | | 3 | Given S2 im Testlauf, When ausgeführt, Then Reports werden erzeugt aber **keine** Mails versendet (`testMode`) | must | | 4 | Given konfigurierte S3, When Scans eintreffen, Then Übersichtstabelle mit Status + Antwortvorschlägen + Mail | must | -| 5 | Given eine Solution, When der Kunde Settings ändert, Then ohne Code-Deploy wirksam (Settings = Daten) | must | +| 5 | Given eine Solution, When der Kunde Settings ändert, Then ohne Re-Publish/Code-Deploy beim nächsten Run wirksam (Injektion, A0.3) | must | | 6 | Given ein neuer ähnlicher Use Case, When aus Template instanziiert, Then nur Settings nötig, kein Code | should | +| 7 | Given S2 über 5 Trustee-Instanzen **einer** Holding, When der Lauf läuft, Then keine Cross-Mandate-Daten; per-Node-Billing bucht Store-Kosten je Store, Holding-Kosten auf Holding (A0.1/A0.2) | must | +| 8 | Given die «Lösungen»-Seite, When sie gerendert wird, Then alle UI-Texte via `t()`, Solution-Name/-Beschreibung lokalisiert (`TextMultilingual`) | must | ## Offene Fragen 1. **S1 Erreichbarkeit:** SelectLine on-prem → Zugriffsweg (VPN/Tunnel/Agent)? Zielart RMA (AR-Rechnung vs. GL-Buchung)? -2. **S5 Migration:** Wie viele der Analyse-Prompt-Templates werden 1:1 Solution-Templates vs. konsolidiert? -3. **S2/S3 Output:** `outputBinding`-Detail (Artefakt-Referenz, `table`-Spalten aus Node-Output ableiten) — siehe step1-architecture «Output-Bindung». -4. **Demo-Daten:** welche Mandanten/Testdaten zuerst (Pling, PWG, Bling)? +2. **S2 Pling-Topologie:** ✅ bestätigt — 5 Trustee-Instanzen *eines* Mandanten (Holding); native Multi-Instanz-Verarbeitung (A0.2). +3. **S5 Umfang:** Welche der vorhandenen Analyse-Templates werden 1:1 als Solution exponiert vs. konsolidiert? +4. **S2/S3 Output:** `outputBinding`-Detail (Artefakt-Referenz, `table`-Spalten aus Node-Output ableiten) — siehe step1-architecture «Output-Bindung». +5. **Demo-Daten:** welche Mandanten/Testdaten zuerst (Pling, PWG, Bling)? ## Links diff --git a/c-work/_CHANGELOG.md b/c-work/_CHANGELOG.md index 43e1475..6f33fc3 100644 --- a/c-work/_CHANGELOG.md +++ b/c-work/_CHANGELOG.md @@ -12,6 +12,11 @@ type: `feat` `fix` `refactor` `docs` `test` `chore` `build` · scope: `platfor Skip: reine Refactors, Formatting, Lint, Dep-Bumps, Test-only, Wiki-Tippfehler. +## 2026-06-05 + +- 2026-06-05 | docs | wiki | **A0 Code-verifiziert korrigiert: Workflow-Ownership = Mandant + Run-as-Principal (nicht Host-Feature)**: Code-Analyse (`executionEngine.py`, `mainScheduler.py`, `routeFeatureGraphicalEditor.py`, `datamodelFeatureGraphicalEditor.py`, Trustee-Actions) zeigt, dass Workflows mandatsweit + identitätsgebunden über beliebige Feature-Instanzen laufen — kein Host-Feature-Owner. **A0.1** neu: Owner-Achse = `mandateId` + `runAsPrincipal`, RBAC = Principal-RBAC; scheduled Runs sollen unter definierter Automations-Identität statt globalem `event`-Sysadmin laufen. **A0.2** neu: Multi-Feature/Multi-Instanz ist nativ (per-Node `FeatureInstanceRef`), echte Schranke = Mandant; 3 Code-Lücken benannt (Runtime-Mandatscheck beim Ref-Auflösen, Read-RBAC analog Write, per-Node-Billing statt fix `graphicalEditor`). **A0.4** neu: `graphicalEditor` als Orchestrierungs-Substrat eingeordnet (kein DATA, eigene DB für alle Features, globaler Scheduler-Boot via `onStart`) — Feature-Mantel bleibt als RBAC-/Store-Eintritt. step1-architecture + product-summary angeglichen. (c-work: c-work/0-ideas/2026-06-CustomerCases-step3-features-plan.md) +- 2026-06-05 | docs | wiki | **CustomerCases step3-Pläne überarbeitet + 3 Architektur-Leitentscheide (A0)**: Nach kritischem Review beide Pläne geschärft. Neuer Abschnitt **A0** im features-plan löst die 3 kritischen Punkte: **A0.1** Solution = `graphicalEditor`-Execution mit `hostFeatureRef` als alleinigem Owner für RBAC/Billing (Cross-Feature-Drift gelöst); **A0.2** Multi-Instanz-Fan-out nur mandatsintern über explizites `instanceSet`, instanz-genaue Billing-Attribution; **A0.3** Settings als Run-Envelope-Injektion (`trigger.form`/DataRef) statt Graph-Mutation (Published-Version-Invariante gewahrt). Zusätzlich: Node-IDs gegen `nodeDefinitions/` verifiziert (neuer `email.sendEmail`-Versand-Node + `data.writeToTable` als Blocker erkannt; `data.consolidate` vorhanden), neuer i18n-Abschnitt (A5, `t()` + `TextMultilingual`), RBAC-Items/Deletion-Cascade/Adapter-Drift ergänzt, S5 als «vorhandene Templates exponieren» (statt migrieren) korrigiert. step1-architecture + product-summary + mockup auf neue Entscheide/Node-IDs angeglichen. (c-work: c-work/0-ideas/2026-06-CustomerCases-step3-features-plan.md, 2026-06-CustomerCases-step3-solutions-plan.md) + ## 2026-06-04 - 2026-06-04 | docs | wiki | **CustomerCases-Doks auf einheitliche Naming-Convention umbenannt**: `2026-06-STEP*`-Dateien → `2026-06-CustomerCases-stepN-*` (step1=architecture, step2=communication [product-summary md/pdf + mockup html], step3=solutions-plan/features-plan). Alle Cross-Referenzen in den Dokumenten + Changelog-Pfade angepasst. (c-work: c-work/0-ideas/2026-06-CustomerCases-step1-architecture.md u. a.)