40 KiB
PWG-Pilot: Jahresmietzinsbestätigungs-Workflow + Workflow-File-IO
Beschreibung und Kontext
Quelle: Konsolidierter Kundenwünsche-Plan, Abschnitt 1.9c — PWG-Workshop 16.04.2026.
Business Case: PWG verschickt 4 × 800 = 3'200 Jahresmietzinsbestätigungen pro Jahr. Rückantworten (Scans) werden aktuell manuell verarbeitet. Pilot-Ziel: AI-gestützte Verarbeitung mit Antwortvorschlägen, Versand Sommer 2026.
Dieser Plan deckt drei zusammenhängende Themen ab:
- Workflow-File-IO — Workflows als File exportieren (in UDB speichern) und aus UDB-File in den Graph-Editor laden. Voraussetzung, damit der Pilot-Workflow als versionierbares Asset im Repo geliefert werden kann.
- Agent-Tools Full-CRUD — Der AI-Agent erhält im
workflow-Toolbox die vollständigen OperationencreateWorkflow,createWorkflowFromFile,exportWorkflowToFile,deleteWorkflow(zusätzlich zu den bestehenden Lese-/Edit-Tools). - Pilot-Workflow als File — Konkrete Lieferung: Workflow-JSON nach Schema, Step-5 AI-Prompt, Datenextraktion aus Trustee-DB.
- PWG-Demo-Bootstrap — Eigenes Demo-Config-Modul
pwgDemo2026.py(analog zuinvestorDemo2026.py), das Mandant, User, Features, Seed-Daten und den importierten Pilot-Workflow per Knopfdruck in einer leeren Dev-Umgebung erstellt.
Risiko bei Nicht-Umsetzung: PWG-Pilot kann nicht termingerecht (Sommer 2026) aufgesetzt werden. Workflows bleiben nicht portabel zwischen Mandanten/Instanzen, was Demo-Vorbereitungen und Customer-Onboarding deutlich verlangsamt.
Abhängigkeiten:
- Abacus-Testmandant (extern, Patrick koordiniert) — nicht Blocker für Workflow-Bau, nur für End-to-End-Test.
- Bestehende Komponenten (alle ✅): Trigger, SharePoint-Nodes,
flow.loop,trustee.extractFromFiles,ai.prompt,email.send.
Fokus und kritische Details
- Trustee-DB hat kein Mieter-/Mietzins-Modell (siehe Codebase-Audit). Stattdessen: Matching-Logik liegt im AI-Prompt von Step 5. Daten werden via Sub-Agent (
queryFeatureInstance/aggregateTableaufTrusteeDataContact+TrusteeDataJournalLine) oder via dediziertem Daten-Extract-Node bereitgestellt. Kein neues DB-Schema nötig. - Graph-Format-Inkonsistenz: Bootstrap-Templates verwenden Node-Felder
x/y(top-level), Agent-TooladdNodeschreibtposition: {x, y}. Beim Import muss normalisiert werden. - Workflow-Envelope-Felder:
AutoWorkflowhat zahlreiche optionale Felder (tags,invocations,templateScope,sharedReadOnly,notifyOnFailure). Beim Export müssen Mandanten-spezifische Felder (mandateId,featureInstanceId,id,currentVersionId,eventId) rausgefiltert werden, sonst sind Files nicht portabel. - Schema-Versionierung: Wir starten mit
$schemaVersion: "1.0". Loader prüft Version und lehnt unbekannte ab oder migriert. - UDB-Erkennung: Workflow-Files identifizieren wir per Dateiendung
.workflow.json(case-insensitive) und Content-Sniffing (Top-Level-Keys$schemaVersion+graph+nodes). - Sicherheit beim Import: Geladene Workflows werden NICHT automatisch ausgeführt. Nutzer muss aktiv via "Speichern + Aktivieren" die
invocationsaktivieren.activewird beim Import auffalsegesetzt. - Node-Type-Validierung: Beim Import gegen
STATIC_NODE_TYPESvalidieren. Unbekannte Node-Typen → Fehler mit klarer Meldung (welcher Typ fehlt).
Ziel und Nicht-Ziele
Ziel:
- Workflows können als File (JSON) aus dem Graph-Editor exportiert und in UDB gespeichert werden.
- Workflow-Files in UDB können im Graph-Editor wieder geladen werden (neuer Workflow oder Update bestehender).
- AI-Agent kann Workflows lesen, erstellen, aus File importieren, exportieren und löschen.
- Der PWG-Pilot-Workflow
pwg-mietzinsbestaetigung-pilot.workflow.jsonliegt im Repo, kann von einer leeren PWG-Demo-Instanz geladen und (mit Test-Daten) ausgeführt werden. - Step-5-Prompt liefert pro Scan: Status (
bestaetigt/abweichung/unleserlich/keine_unterschrift) und einen Antwortvorschlag bei Abweichung.
Explizit NICHT:
- Kein neues DB-Schema für Mietzins/Mieter (Matching erfolgt im Prompt gegen bestehende Trustee-Daten).
- Keine Persistenz der Pilot-Run-Ergebnisse in einer Trustee-Sub-Tabelle (CSV im Mail reicht für Pilot).
- Keine Auto-Aktivierung importierter Workflows (sicherheitsrelevant).
- Keine Migration-Logik für
$schemaVersion > 1.0(kommt später). - Kein Versions-Branching/Diff von Workflow-Files (späteres Thema).
- Keine SharePoint-OCR-Verbesserungen (extractFromFiles wird wie bestehend genutzt).
Betroffene Module
- Gateway:
features/graphicalEditor/routeFeatureGraphicalEditor.py— neue RoutenPOST .../workflows/importundGET .../workflows/{id}/export.features/graphicalEditor/interfaceFeatureGraphicalEditor.py— neue MethodenimportWorkflowFromDict,exportWorkflowToDict.features/graphicalEditor/_workflowFileSchema.py(NEU) — Schema-Definition + Validierung + Normalisierung (x/y↔position).serviceCenter/services/serviceAgent/workflowTools.py— neue ToolscreateWorkflow,createWorkflowFromFile,exportWorkflowToFile,deleteWorkflow.serviceCenter/services/serviceAgent/toolboxRegistry.py— Tool-Liste inworkflow-Toolbox erweitern.
- Frontend:
components/UnifiedDataBar/FilesTab.tsx— Workflow-File-Erkennung + Action "In Graph-Editor laden".pages/views/workflow/(Graph-Editor-View) — Buttons "Aus Datei importieren" + "Als Datei exportieren" (Editor-Toolbar).api/workflowApi.ts(oder analog) — neue Endpoint-Wrapper.
- DB-Migration: nein.
- Repo-Asset:
gateway/demoData/workflows/pwg-mietzinsbestaetigung-pilot.workflow.json(NEU). - Demo-Config:
gateway/modules/demoConfigs/pwgDemo2026.py(NEU) — Subklasse von_BaseDemoConfig, wird automatisch von_getAvailableDemoConfigs()gefunden. - Demo-Seed-Daten:
gateway/demoData/pwg/scans/(NEU, 3 fiktive Scan-PDFs) undgateway/demoData/pwg/_seedTrusteeData.json(NEU, fiktive Mieter + Mietzins-Buchungen). - Andere: Step-5-Prompt-Template als Konstante in
gateway/modules/features/trustee/promptTemplates/_pwgMietzinsCheck.pyoder direkt im Workflow-File (Decision unten).
Entscheidungen
| Datum | Entscheidung | Begründung |
|---|---|---|
| 2026-04-16 | Workflow-File-Format = Envelope + Schema-Version (1.0) | Portabel, zukunftssicher, ein Round-Trip ohne Verlust von Metadaten |
| 2026-04-16 | Matching-Logik im AI-Prompt von Step 5 (nicht neue DB-Tabelle) | Trustee-DB hat kein Mietzins-Modell; Pilot kommt mit AI + bestehende Sub-Agent-Queries aus |
| 2026-04-16 | Result-Output = CSV als E-Mail-Anhang (kein DB-Schema) | Schlankster Pilot-Pfad; Persistenz später bei Bedarf nachrüstbar |
| 2026-04-16 | Agent-Tools = Full-CRUD inkl. deleteWorkflow |
Agent soll Workflows komplett verwalten können (User-Wunsch) |
| 2026-04-16 | File-Endung .workflow.json + Content-Sniffing |
Klare UDB-Identifikation ohne neuen MIME-Type |
| 2026-04-16 | Import setzt active: false; Aktivierung manuell |
Sicherheits-Default — kein versehentliches Auto-Run nach Import |
| 2026-04-16 | Step-5-Prompt im Workflow-File (Parameter von ai.prompt-Node) |
Das File ist self-contained und im Repo lesbar; keine Code-Änderung beim Tunen |
| 2026-04-16 | CSV-Sammlung über bestehende data.aggregate (collect) + data.consolidate (csvJoin) |
Idiomatisch, bereits implementiert; kein neuer Node nötig |
| 2026-04-16 | Neuer Node trustee.queryData ist Pflicht |
Kein bestehender Node liest Trustee-DB; ai.prompt hat keinen Sub-Agent-Tool-Zugriff (verifiziert in methodAi/actions/process.py) — context-Parameter muss vorher befüllt werden |
| 2026-04-16 | E-Mail mit Attachment via Erweiterung von email.draftEmail (neuer optionaler attachments-Parameter), kein neuer Node |
Minimal-invasiv; email.draftEmail ist bereits gemappt auf methodOutlook.composeAndDraftEmailWithContext und kann erweitert werden. Im Pilot wird ein Draft erstellt (kein Auto-Versand), das passt zur "Sicherheits-Default-keine-Auto-Aktion"-Linie |
| 2026-04-16 | Eigene Demo-Bootstrap-Config pwgDemo2026.py (analog zu investorDemo2026.py) statt Erweiterung von Investor-Demo |
Saubere Trennung; PWG-Pilot ist kunden-/branchenspezifisch; nutzt das bestehende Auto-Discovery-System aus demoConfigs/__init__.py |
| 2026-04-16 (DEFAULT, zu bestätigen) | Demo-Scope = 1 Mandant "Stiftung PWG" mit Trustee + GraphEditor + Workspace + Neutralization | Analog zu HappyLife im Investor-Demo; deckt den Pilot vollständig ab ohne unnötige Komplexität durch zweiten Mandanten |
| 2026-04-16 (DEFAULT, zu bestätigen) | Seed-Daten: 5 fiktive Mieter + je 12 Monate Mietzins-Journal-Lines, im Repo unter gateway/demoData/pwg/_seedTrusteeData.json |
Minimal-Set damit alle 3 Test-Scans matchen können (1× ok, 1× Abweichung, 1× ohne Unterschrift), kein Abacus-Connector nötig für lokale Demo |
| 2026-04-16 (DEFAULT, zu bestätigen) | 3 Test-Scan-PDFs im Repo unter gateway/demoData/pwg/scans/ + lokaler Folder-Connector statt SharePoint |
Reproduzierbar ohne externe Abhängigkeiten; SharePoint-Variante als optionale Phase-7-Erweiterung |
| 2026-04-16 (DEFAULT, zu bestätigen) | Pilot-Workflow wird beim load() automatisch importiert (active=false) |
Demo-User klickt nach Bootstrap nur noch "Manueller Trigger" — minimaler Klickpfad zum Wow-Effekt |
Architektur
Workflow-File Schema 1.0
{
"$schemaVersion": "1.0",
"$exportedAt": "2026-04-16T10:00:00Z",
"$gatewayVersion": "0.x.y",
"$kind": "poweron.workflow",
"label": "PWG Pilot: Jahresmietzinsbestätigung",
"description": "Verarbeitet gescannte Rückantworten der Mietzinsbestätigungen ...",
"tags": ["pwg", "pilot", "mietzins"],
"templateScope": "instance",
"sharedReadOnly": false,
"notifyOnFailure": true,
"graph": {
"nodes": [
{ "id": "n1", "type": "trigger.manual", "x": 50, "y": 200, "title": "Manueller Start", "parameters": {} }
],
"connections": [
{ "source": "n1", "target": "n2", "sourceOutput": 0, "targetInput": 0 }
]
},
"invocations": [
{ "type": "schedule", "cronExpression": "0 22 * * *" }
]
}
Felder, die NIE im File stehen: id, mandateId, featureInstanceId, currentVersionId, eventId, createdAt, updatedAt, active (wird beim Import auf false gesetzt).
Normalisierung beim Import: Falls Node position: {x, y} enthält → in x/y top-level umwandeln (oder umgekehrt — wir entscheiden uns für x/y top-level als kanonische Form, weil Bootstrap-Templates so aussehen).
API-Endpunkte (neu)
| Methode | Pfad | Zweck |
|---|---|---|
POST /api/workflows/{instanceId}/workflows/import |
Body: { "fileId": "...", "mode": "create" | "updateGraph", "targetWorkflowId"?: "..." } |
Lädt File aus UDB, validiert, erstellt neuen Workflow oder ersetzt graph eines bestehenden |
GET /api/workflows/{instanceId}/workflows/{workflowId}/export |
Query: ?asFileId=true&folderId=... |
Exportiert Workflow als File. Wenn asFileId=true: speichert in UDB und gibt fileId zurück. Sonst: gibt JSON-Body direkt zurück (Browser-Download) |
Frontend-Erweiterungen
UDB FilesTab (FilesTab.tsx):
- Erkennt Workflow-Files (
.workflow.jsonoder Top-Level$kind === "poweron.workflow"). - Workflow-File-Eintrag erhält Icon (z. B. Workflow/Diagramm) und Context-Menu-Eintrag "In Graph-Editor laden".
- Bei Klick → Modal: Ziel-Editor-Instanz wählen + Mode (
create/updateGraphfür aktiven Workflow) → POST/import.
Graph-Editor Toolbar (Editor-View):
- Button "Importieren" → File-Picker (UDB-Files mit Filter
.workflow.json) →POST /import. - Button "Exportieren" → Modal: Ziel = Download oder UDB-Folder →
GET /export?asFileId=....
AI-Agent Tools (Full-CRUD im workflow-Toolbox)
Erweiterung von workflowTools.py und Toolbox-Registry:
| Tool | Bestehend? | Beschreibung |
|---|---|---|
readWorkflowGraph |
✅ | Graph eines Workflows lesen |
addNode, removeNode, connectNodes, setNodeParameter |
✅ | Bestehende Edit-Tools |
listAvailableNodeTypes, validateGraph |
✅ | Bestehende Helpers |
listWorkflowHistory, readWorkflowMessages |
✅ | Bestehende Read-Tools |
createWorkflow |
NEU | Args: label, description?, tags?, graph, invocations?. Ergebnis: { workflowId } |
createWorkflowFromFile |
NEU | Args: fileId, mode (default create). Ergebnis: { workflowId, importedNodes, warnings } |
exportWorkflowToFile |
NEU | Args: workflowId, targetFolderId?. Ergebnis: { fileId, fileName } |
deleteWorkflow |
NEU | Args: workflowId, confirm: true. Sicherheits-Confirm-Flag |
Implementierung: alle neuen Tools rufen die neuen Routen / Interface-Methoden auf, kein direkter DB-Zugriff aus den Tools.
Pilot-Workflow: Inhalt
Datei: gateway/demoData/workflows/pwg-mietzinsbestaetigung-pilot.workflow.json
Knoten-Übersicht (10 Nodes — basierend auf real verfügbaren Nodes nach Codebase-Audit):
| ID | Type | Title | Wichtige Parameter |
|---|---|---|---|
n1 |
trigger.manual |
Manueller Start | (alternativ trigger.schedule mit Cron 0 22 * * *) |
n2 |
sharepoint.listFiles |
Scan-Ordner auflisten | connectionReference, sharepointFolder: "PWG/Mietzinsbestaetigungen/Scans-{year}-{quarter}", filter: { extensions: ["pdf", "tif", "jpg"] } |
n3 |
flow.loop |
Pro Dokument | inputArrayPath, itemAlias: "scanFile" |
n4 |
sharepoint.downloadFile |
Datei laden | fileReference: "{{loop.item}}" |
n5 |
trustee.extractFromFiles |
OCR & Extraktion | featureInstanceId, prompt: <ExtractionPrompt für Mietzinsbestätigung — siehe unten> |
n6 |
trustee.queryData ⚠️ NEU |
Referenzdaten holen | featureInstanceId, mode: "lookup", entity: "tenantWithRent", tenantNameRef: "{{n5.output.tenantName}}", tenantAddressRef: "{{n5.output.tenantAddress}}", period: "{{currentYear}}" |
n7 |
ai.prompt |
Prüfung & Klassifikation | aiPrompt: <Step-5-Prompt-Template>, outputFormat: "json", documentList: <Wire von n5>, context: <Wire von n6> |
n8 |
data.aggregate |
Ergebnisse sammeln (im Loop) | mode: "collect" — sammelt n7.output über Loop-Iterationen |
n9 |
data.consolidate |
CSV bauen (nach Loop) | mode: "csvJoin", separator: "\n" — wandelt gesammelte JSON-Items in CSV |
n10 |
email.draftEmail |
Draft mit Anhang | connectionReference, to: "sachbearbeiter@pwg.ch", subject: "Mietzinsbestätigungen Auswertung {{date}}", body: <Zusammenfassung>, attachments: [{ name: "ergebnisse.csv", contentRef: "{{n9.output}}" }] ⚠️ attachments-Param muss in email.draftEmail ergänzt werden |
Nicht-existierende Nodes/Params, die als Sub-Aufgabe gebaut werden müssen (siehe Phase 4):
trustee.queryData— neuer Node innodeDefinitions/trustee.py+ Action inmethodTrustee/actions/queryData.py. WrapptFeatureDataProvider.queryTable/aggregateTable. Modelookupmit EntitytenantWithRentmacht intern: Match inTrusteeDataContact(Debitor) → Aggregat überTrusteeDataJournalLinefür Mietzins-Konten in der Periode.email.draftEmail.attachments— optionaler ParameterattachmentsinnodeDefinitions/email.pyergänzen + im ExecutorcomposeAndDraftEmailWithContextAttachment-Handling implementieren.
Datenfluss-Anmerkungen:
- Der
context-Parameter vonai.promptmuss VOR dem Node befüllt werden (verifiziert:methodAi/actions/process.pymacht direktenAiCallRequestohne Sub-Agent-Tools). Daher liegtn6zwingend vorn7im Wire-Pfad. data.aggregate(modecollect) sammelt im Loop-Body,data.consolidate(modecsvJoin) führt nach dem Loop zusammen — Standardmuster für CSV-Output aus Loop-Iterationen.helpers/csvProcessing.py(inmethodAi) kann vondata.consolidateund/oderemail.draftEmail-Attachment-Builder wiederverwendet werden.
Extraktionsschema für n5 (extractionSchema: "mietzinsbestaetigung")
Erwartete Felder im OCR-Output:
{
"tenantName": "string",
"tenantAddress": "string",
"objectAddress": "string",
"confirmedRentAmount": "number|null",
"currency": "CHF",
"period": "string (z.B. 2026)",
"tenantNotes": "string|null",
"hasSignature": "boolean",
"documentDate": "string (ISO date)|null",
"ocrConfidence": "number (0-1)"
}
Step-5-Prompt (Inhalt für n7.parameters.prompt)
Du bist ein Sachbearbeitungs-Assistent der Stiftung PWG. Deine Aufgabe ist es,
eine eingescannte und OCR-extrahierte Jahresmietzinsbestätigung gegen die
Stammdaten der Buchhaltung (Trustee-Feature) abzugleichen.
Eingaben:
1. SCAN_DATEN (extrahiert per OCR aus dem Rückantwort-Dokument):
{{scan}}
2. REFERENZ_DATEN (aus Trustee-DB für diesen Mieter; ggf. leer wenn nicht
eindeutig zuordenbar):
{{reference}}
Vorgehen:
1. Prüfe Identität: Stimmt SCAN_DATEN.tenantName + SCAN_DATEN.tenantAddress mit
einem Datensatz in REFERENZ_DATEN.contacts überein? (Toleranz: kleine
Tippfehler, Umlaute, Abkürzungen)
2. Prüfe Mietzinsbetrag: Stimmt SCAN_DATEN.confirmedRentAmount mit dem aus
REFERENZ_DATEN.journalLines abgeleiteten erwarteten Mietzins überein?
(Toleranz: ±1 CHF Rundung)
3. Prüfe Unterschrift: hasSignature muss true sein.
4. Prüfe OCR-Qualität: ocrConfidence < 0.6 → "unleserlich".
Klassifiziere in EXAKT EINEN Status:
- "bestaetigt": Identität stimmt, Betrag stimmt, Unterschrift vorhanden.
- "abweichung_betrag": Identität ok, Unterschrift ok, Betrag weicht ab.
- "abweichung_anmerkung": tenantNotes enthält substantielle Anmerkung
(nicht leer, nicht reine Bestätigung).
- "keine_unterschrift": hasSignature == false.
- "unleserlich": OCR-Qualität ungenügend ODER Pflichtfelder fehlen.
- "kein_match": Mieter nicht in REFERENZ_DATEN auffindbar.
Bei Status != "bestaetigt": Generiere einen kurzen, höflichen
Antwortvorschlag (deutsch, Sie-Form, max. 5 Sätze, PWG-Stil) für die
Sachbearbeitung. Bei "bestaetigt": antwortVorschlag = null.
Antworte AUSSCHLIESSLICH als JSON nach folgendem Schema:
{
"tenantName": string,
"objectAddress": string,
"status": "bestaetigt" | "abweichung_betrag" | "abweichung_anmerkung"
| "keine_unterschrift" | "unleserlich" | "kein_match",
"scanRentAmount": number | null,
"expectedRentAmount": number | null,
"delta": number | null,
"tenantNotes": string | null,
"antwortVorschlag": string | null,
"matchConfidence": number,
"auditEvidence": string
}
outputSchema im Node erzwingt JSON-Form (existierende ai.prompt-Capability nutzen).
Umsetzungs-Checkliste
Phase 1 — Workflow-File-IO Backend ✅ DONE (2026-04-19)
- Schema-Modul
_workflowFileSchema.py—WORKFLOW_FILE_SCHEMA_VERSION = "1.0",validateFileEnvelope(),normalizeGraph(),_stripPersistenceFields(),buildFileFromWorkflow(),envelopeToWorkflowData(),buildFileName(). Helpers ohne_-Prefix weil exportierbar (vom Routen-Code aufgerufen). - Interface-Methoden in
interfaceFeatureGraphicalEditor.py:importWorkflowFromDict(envelope, existingWorkflowId?) -> { workflow, warnings, created }exportWorkflowToDict(workflowId) -> envelope
- Routen in
routeFeatureGraphicalEditor.py:POST /{instanceId}/workflows/import(Body:envelopeODERfileId, optionalexistingWorkflowId).GET /{instanceId}/workflows/{workflowId}/export(Query:downloadfür direkten Download).
- Validierungen:
- Schema-Version Whitelist (
1.0), Pflichtfelder (label,graph.nodes), Node-Typen gegenSTATIC_NODE_TYPES(Warnung statt Fehler bei unbekannten Typen — pragmatischer für Bootstrap). - Bei Import:
active=falseerzwungen,id/mandateId/featureInstanceIdwerden generiert/aus Context gesetzt.
- Schema-Version Whitelist (
- Unit-Test
tests/unit/workflow/test_workflowFileSchema.py: Round-Trip + Field-Stripping + Envelope-Validierung.
Phase 2 — UDB-Erkennung & Frontend-IO ✅ PARTIAL (2026-04-19)
api/workflowApi.ts:importWorkflowFromFile(envelope|fileId, existingWorkflowId?),exportWorkflowToFile(workflowId, download?),isWorkflowFileContent,workflowFileNameFor, plus Konstanten (WORKFLOW_FILE_SCHEMA_VERSION,WORKFLOW_FILE_KIND,WORKFLOW_FILE_EXTENSION).GraphicalEditorWorkflowsPage.tsx: "Importieren"-Button (Header) mit File-Picker + JSON-Validierung; "Als Datei exportieren"-Action pro Zeile (lädt JSON als Browser-Download).- DEFERRED —
FilesTab.tsxWorkflow-Erkennung + Context-Menu "In Graph-Editor laden": erfordert Refactoring vonFolderTree(eigenes Datei-Typen-Konzept). Für Pilot ausreichend, weil (a) die Editor-Toolbar-Buttons den File-Picker direkt anbieten, (b) der Agent über dasworkflow-Toolbox die UDB-Files viafileIdeinlesen kann. Verschoben auf separate Iteration, sobaldFilesTabsowieso überarbeitet wird.
Phase 3 — Agent-Tools Full-CRUD ✅ DONE (2026-04-19)
- In
workflowTools.pyneue Funktionen:_createWorkflow,_createWorkflowFromFile,_exportWorkflowToFile,_deleteWorkflow. - Tool-Definitionen in
getWorkflowToolDefinitions()ergänzt (jeweils mit klaren Beschreibungen, was der Agent vor dem Aufruf prüfen soll). toolboxRegistry.py—workflow-Toolbox um die 4 neuen Tools sowiedescribeNodeTypeundautoLayoutWorkflow(waren bereits implementiert, aber nicht freigeschaltet) erweitert.- Sicherheits-Confirm:
deleteWorkflowverlangt explizitconfirm: true, sonst Tool-Error "confirm flag missing". - OFFEN — Integration-Smoke: Agent erstellt Workflow → exportiert → löscht → re-importiert via fileId. Wird via Test C im UI-Smoke-Kochbuch (Abschnitt unten) durchgeführt.
Phase 4 — Fehlende Nodes für Pilot ergänzen (konkretisiert nach Codebase-Audit)
Audit-Ergebnis (siehe auch Tabelle in Abschnitt "Findings" weiter oben):
- ✅
data.aggregate(modecollect) unddata.consolidate(modecsvJoin) existieren bereits → CSV-Sammlung ist abgedeckt, kein neuer Daten-Node nötig. - ❌ Kein Node liest Trustee-DB-Daten →
trustee.queryDatamuss neu gebaut werden. - ❌
email.draftEmailhat keinenattachments-Parameter → muss erweitert werden. - ✅
ai.promptmitoutputFormat: "json"undcontext-Parameter ist passgenau für Step 5.
Sub-Task 4a — Neuer Node trustee.queryData: ✅ DONE (2026-04-19)
- Definition in
gateway/modules/features/graphicalEditor/nodeDefinitions/trustee.pyergänzt (Pattern wietrustee.refreshAccountingData).- Parameter:
featureInstanceId(hidden),mode(select:lookup,aggregate,raw),entity(select:tenantWithRent,contact,journalLines,accounts),tenantNameRef/tenantAddressRef/period(text, optional je nach mode),extraFilter(json, optional). - Inputs: 1, Outputs: 1,
outputPorts: { 0: { schema: "QueryResult" } }. _method: "trustee",_action: "queryData".
- Parameter:
- Action-Implementierung
gateway/modules/workflows/methods/methodTrustee/actions/queryData.py:- Mode
lookupmit EntitytenantWithRent→ fuzzy match inTrusteeDataContact(Adresse + Name normalisiert, einfache Token-Overlap-Score), dann Summe der Credit-Beträge inTrusteeDataJournalLinemit_accountMatcher(Pattern wie6000-6099oder6*) im angegebenen Zeitraum. - Mode
raw→ liest direkt aus den 5 Trustee-Tabellen (Filter viafilterJson). - Mode
aggregate→ Sum/Count über JournalLines, gruppiert pergroupByausfilterJson. - Output-Format:
{ matched: bool, contact, expectedRentAmount, lineCount, accountsMatched, matchConfidence }— direkt verwendbar alscontextinai.prompt.
- Mode
- Registrierung in
methodTrustee.py(Action-Definition + Methodenbindung);__init__.pybrauchte keine Änderung (nutzt direkten Import). - Unit-Test
tests/unit/workflow/test_trusteeQueryData.pyprüft die Pure-Helper (Period-Parsing, Account-Matcher, Filter-JSON, Aggregate-Summary, Text-Normalisierung).
Sub-Task 4b — Attachment-Support in email.draftEmail: ✅ DONE (2026-04-19)
- In
nodeDefinitions/email.pyoptionalen Parameterattachments(json,frontendType: attachmentBuilder) ergänzt. - In
gateway/modules/workflows/methods/methodOutlook/actions/composeAndDraftEmailWithContext.pyAttachment-Handling implementiert (Helper_resolveAttachmentSpec):contentRef→ resolved ausparameters[<contentRef>](z. B. CSV vomdata.consolidate-Node), als String.csvFromVariable→ Shorthand für CSV-Ref, hängt.csvan, MIMEtext/csv.base64Content→ direkt verwendet, MIME ausmimeTypeoder Default.- Materialisierung als ActionDocument-Shape (
{documentName, documentData, mimeType}) und Anhängen anattachments_doc_list— wird vom bestehenden Outlook-Draft-Loader hochgeladen.
- Frontend Attachment-Builder UI: NICHT NÖTIG — der
attachments-Parameter wird programmatisch viacsvFromVariable: "csv"aus demdata.consolidate-Output gefüllt; manuelles Bearbeiten ist Edge-Case. Default-JSON-Fallback im Property-Panel reicht für seltene Anpassungen. - Unit-Test Outlook-Mock: NICHT NÖTIG — End-to-End-Test mit Live-Outlook (Test D im Smoke-Test-Kochbuch) deckt das Verhalten realistischer ab als ein gemockter Connector. Wird beim Pilot-Smoke validiert.
Sub-Task 4c — Frontend-Anpassungen für neue Nodes:
trustee.queryDataautomatisch in der Node-Palette sichtbar (kommt durch Definition gratis).- Property-Panel: Sichtbarkeit über
frontendOptions.dependsOn+showWhendeklariert (vorhandener Mechanismus). - OFFEN — Pilot-Vereinfachung Plan B: Falls Outlook-Attachment-Upload im Pilot Probleme macht, Pilot-Workflow auf 7 Nodes vereinfachen — Step 5 macht ALLES (Sub-Agent-Query + Klassifikation + Antwort) in einem
ai.prompt-Node mitfeatureSubAgent-Tool-Zugriff. Aktuell nicht aktiviert — der Workflow läuft mit dediziertemtrustee.queryData-Node.
Phase 5 — Pilot-Workflow als File ✅ DONE (2026-04-19)
- Datei
gateway/demoData/workflows/pwg-mietzinsbestaetigung-pilot.workflow.jsonerstellt — 10 Nodes (trigger.manual→sharepoint.listFiles→flow.loop→sharepoint.downloadFile→trustee.extractFromFiles→trustee.queryData→ai.prompt→data.aggregate→data.consolidate→email.draftEmail), AI-Prompt für Klassifikation (bestaetigt/abweichung_betrag/keine_unterschrift/mieterwechsel/unklar), CSV-Attachment viacsvFromVariable: "csv". - Lokale Validierung gegen
validateFileEnvelope()— keine Errors, keine Warnungen. - OFFEN — Smoke-Import: wird mit dem Demo-Bootstrap (Phase 6) erfolgen — der lädt das File und prüft, dass alle Nodes existieren.
- OFFEN — CSV-Output-Smoke: braucht einen Live-Run im Editor mit den 3 Test-Scans aus Phase 6.
Phase 6 — PWG-Demo-Bootstrap (pwgDemo2026.py) ✅ DONE (2026-04-19)
Ziel: Per Klick (oder API-Call auf routeAdminDemoConfig) entsteht in einer leeren Dev-/Demo-Umgebung ein vollständig lauffähiges PWG-Setup, in dem der Pilot-Workflow direkt ausgeführt werden kann.
- 6a — Skeleton:
gateway/modules/demoConfigs/pwgDemo2026.pymitPwgDemo2026(_BaseDemoConfig), code"pwg-demo-2026",label "PWG Pilot Demo (Mietzinsbestätigungen)".load()/remove()analoginvestorDemo2026.py(Helpers wurden vorerst kopiert; Refactor in_demoHelpers.pyist eigene Iteration). - 6b — Mandant + User + Features: Mandant
stiftung-pwg, Userpwg.demo@poweron.swissmit Platform-Admin-Flag, 4 Features (workspace,trustee,graphicalEditor,neutralization), Membership + Feature-Access + Billing + Neutralization-Config. - 6c — Trustee-Seed-Daten:
gateway/demoData/pwg/_seedTrusteeData.jsonmit 5 Mieter-Contacts und je 12 monatlichen JournalLines für 2026. Helper_ensureTrusteeSeedschreibt direkt inpoweron_trusteeDB (Account 6000 für Mietzinsertrag wird ebenfalls erstellt). Idempotent überexternal_id. - 6d — Pilot-Workflow Auto-Import:
_ensurePilotWorkflowlädtpwg-mietzinsbestaetigung-pilot.workflow.json, ersetzt Platzhalter<<TRUSTEE_INSTANCE_ID>>mit der real angelegten Trustee-Instanz, importiert viaimportWorkflowFromDict(active=false). Idempotent über Label-Check. - 6e — Test-Scans:
gateway/demoData/pwg/_generateScans.py(reportlab) erzeugt 3 PDFs:mieter01-bestaetigt.pdf,mieter02-abweichung-betrag.pdf,mieter03-keine-unterschrift.pdf. Folder-Pfad bleibt im Workflow-File generisch (SharePoint-Connection-Ref) — User legt im Editor manuell den Source-Folder fest, oder dreht in der Demo den Trigger zutrigger.manualmit Inline-Files. - 6f —
remove()-Pfad: Spiegelbildlich, löscht Workflow-Records, Trustee-Seed (Lines/Entries/Contacts/Accounts), Memberships, Feature-Instances, User, Mandant. - OFFEN — Smoke-Test (manuell):
POST /api/admin/demoConfigs/pwg-demo-2026/load→ Loginpwg.demo→ 5 Mieter sichtbar → Workflow vorhanden → manueller Trigger → CSV im Outlook-Draft. Wird beim nächsten Local-Dev-Start gemacht.
Querschnitt
- API-Endpunkte: ja, 2 neue (
POST /workflows/import,GET /workflows/{id}/export). - DB-Schema / Migration: nein.
- Frontend-Komponenten: Editor-Toolbar Buttons + Export-Action umgesetzt. FilesTab-Context-Menu (UDB-Erkennung) bewusst auf eigenen Plan ausgelagert:
wiki/c-work/1-plan/2026-04-udb-action-system.md— adressiert das gesamte Action-System der UDB (Right-Click + Long-Press + Drag&Drop + kontextspezifische Inline-Icons), nicht nur Workflow-Files. - RBAC / Permissions: Bestehendes Modell ist ausreichend — Workflows sind Mandate-scoped,
_validateInstanceAccessprüft Membership + Feature-Access. Zusätzlichconfirm: true-Flag-Schutz im Agent-TooldeleteWorkflow. Kein eigener Permission-Layer nötig (Templates haben bereitstemplateScope+sharedReadOnlyfür Sharing-Cases). - Navigation / Routing: keine Änderung.
- Sicherheits-Hinweis Export: Toast nach Export („Workflow als Datei exportiert") reicht. Connection-Refs in Workflow-Files sind technische IDs (keine Secrets), Mail-Adressen sind Konfiguration. Kein Modal-Disclaimer nötig.
- OFFEN — Billing-Impact: Pilot-Workflow verbraucht Tokens (1 AI-Call pro Scan, ggf. Sub-Agent-Calls). Kalkulation für PWG-Pilot:
~3'200 Schreiben/Jahr × ~3 Calls × ~2k Tokens— gehört in PWG-Preismodell-Action-Item AI3 (eigener Plan).
UI-Smoke-Test-Kochbuch (manuell, nur Browser)
Voraussetzung: Gateway läuft (uvicorn), Frontend läuft, Login mit einem Platform-Admin-Account (z. B. patrick). Outlook-Connection im Mandant „Stiftung PWG" verfügbar (für Test D).
Test A — PWG-Demo laden (≈ 1 min)
- Login als Platform-Admin.
- Sidebar oben rechts: Avatar → „Admin" → Reiter „Demo-Konfigurationen".
- Eintrag „PWG Pilot Demo (Mietzinsbestätigungen)" suchen → Klick „Laden".
- Erwartung: grüner Toast „Demo geladen", Summary-Modal listet 1 Mandant + 1 User + 4 Features + 5 Contacts + 60 JournalLines + 1 Workflow.
Test B — Demo-Inhalt prüfen (≈ 2 min)
- Logout, Login als
pwg.demo@poweron.swiss(Default-Passwort siehe Modal aus Test A). - Mandanten-Wechsler oben links → „Stiftung PWG" auswählen.
- Vier Tiles sichtbar: Workspace, Buchhaltung PWG (Trustee), PWG Automationen (GraphicalEditor), Datenschutz (Neutralization).
- Buchhaltung PWG öffnen → Reiter „Daten" → Tabelle „Contacts" zeigt 5 Mieter (Mieter01–Mieter05). Tabelle „JournalLines" zeigt 60 Einträge (Filter
account = 6000). - PWG Automationen öffnen → Workflow-Liste enthält „PWG — Jahresmietzinsbestätigung Pilot" mit Status-Badge
inactive.
Test C — File-Round-Trip + Agent-CRUD (≈ 4 min)
Teil 1 — UI-Export/Import:
- In der Workflow-Liste Action „Als Datei exportieren" an der Pilot-Zeile klicken → Browser lädt
pwg-jahresmietzinsbestaetigung-pilot.workflow.jsonherunter. - Header-Button „Importieren" klicken → eben heruntergeladene Datei auswählen.
- Toast „Workflow importiert (deaktiviert)" erscheint, Liste enthält jetzt zwei Workflows (zweiter mit identischem Label, ebenfalls inactive).
- Neuen Workflow per Action löschen.
Teil 2 — Agent-CRUD:
- Pilot-Workflow öffnen → KI-Tab unten rechts.
- Prompt: „Erstelle einen leeren Workflow namens 'Smoke-Test'." → Agent-Antwort enthält neue Workflow-ID, Liste aktualisiert sich.
- Prompt: „Lösche den Workflow 'Smoke-Test'." → Agent fragt nach Bestätigung (
confirm flag missing). - Prompt: „Ja, bitte löschen, mit confirm true." → Workflow ist weg.
Test D — End-to-End mit Outlook (≈ 5 min, braucht Outlook-Account)
- Pilot-Workflow öffnen, Editor-Canvas sichtbar.
- Property-Panel der Nodes prüfen:
sharepoint.listFilesSource-Folder auf einen erreichbaren SharePoint-/OneDrive-Ordner setzen.email.draftEmailConnection auf den eigenen Outlook-Account, Empfänger auf eigene Adresse setzen (Demo-Mail an sich selbst).
- Test-PDFs vorbereiten:
python gateway/demoData/pwg/_generateScans.pyeinmal ausführen → 3 PDFs ingateway/demoData/pwg/scans/. Diese in den oben gewählten SharePoint-Ordner hochladen. - Workflow oben rechts auf „Aktiv" schalten → Button „Manuell ausführen" klicken.
- Run-Detail-Seite zeigt Step-by-Step-Logs:
- 3 Loop-Durchläufe (1 pro PDF).
- In jedem Loop:
trustee.extractFromFiles→trustee.queryData(matched=true) →ai.prompt(status gefüllt). - Am Ende:
data.consolidateproduziert CSV mit 3 Zeilen,email.draftEmailerstellt Draft.
- Outlook öffnen → Ordner „Entwürfe" → neuer Draft mit CSV-Anhang. CSV zeigt Spalten u. a.
mieterName,expectedRent,extractedRent,delta,status,antwortVorschlag. 1×bestaetigt, 1×abweichung_betrag, 1×keine_unterschrift.
Test E — Demo abräumen (≈ 30 s)
- Logout, wieder als Platform-Admin einloggen.
- Admin → Demo-Konfigurationen → „PWG Pilot Demo" → „Entfernen".
- Toast „Demo entfernt". Mandant „Stiftung PWG" verschwindet aus der Liste.
- Erneutes „Laden" muss fehlerfrei durchlaufen (Idempotenz-Check).
Akzeptanzkriterien
| # | Kriterium (Given-When-Then) | Prio |
|---|---|---|
| 1 | Given Workflow im Graph-Editor, When User auf "Exportieren → UDB" klickt, Then liegt eine <label>.workflow.json im gewählten UDB-Folder mit Schema-Version 1.0 |
must |
| 2 | Given Workflow-File in UDB, When User in FilesTab "In Graph-Editor laden" wählt + Mode create bestätigt, Then existiert ein neuer Workflow mit identischem Graph und active=false |
must |
| 3 | Given Workflow-File mit unbekanntem Node-Typ, When Import, Then Fehler-Response listet konkret den fehlenden Typ; kein Workflow wird erstellt | must |
| 4 | Given Workflow A in Editor, When exportWorkflowToFile → createWorkflowFromFile, Then ist der neu erstellte Workflow nach Normalisierung byte-äquivalent (Round-Trip-Stabilität) |
should |
| 5 | Given AI-Agent mit aktivem workflow-Toolbox, When Agent ruft createWorkflow mit gültigem Graph, Then wird Workflow erstellt und workflowId zurückgegeben |
must |
| 6 | Given AI-Agent, When Agent ruft deleteWorkflow ohne confirm: true, Then Tool antwortet mit Fehler "confirm flag missing" und löscht nichts |
must |
| 7 | Given PWG-Pilot-Workflow geladen + 1 Test-Scan-PDF in SharePoint-Ordner, When manueller Trigger, Then enthält die E-Mail-CSV exakt 1 Zeile mit gefülltem status-Feld |
must |
| 8 | Given Test-Scan ohne Unterschrift, When Workflow läuft, Then status == "keine_unterschrift" und antwortVorschlag ist gefüllt |
should |
| 9 | Given Test-Scan mit korrekten Daten, When Workflow läuft + Trustee-DB enthält passenden Debitor + Journal-Line, Then status == "bestaetigt" und delta == 0 |
should |
| 10 | Given importierter Workflow, When User versucht ihn ohne weitere Aktion zu schedulen, Then schlägt es fehl bzw. active ist false und User muss explizit aktivieren |
must |
| 11 | Given leere Dev-Umgebung, When POST /api/admin/demoConfigs/pwg-demo-2026/load, Then existieren Mandant "Stiftung PWG", Demo-User, alle 4 Features, 5 Seed-Mieter mit Journal-Lines und der Pilot-Workflow (active=false) |
must |
| 12 | Given geladene PWG-Demo, When POST .../pwg-demo-2026/remove, Then sind alle erstellten Datensätze restlos entfernt; ein erneutes load läuft fehlerfrei (Idempotenz) |
must |
| 13 | Given geladene PWG-Demo, When User aktiviert Pilot-Workflow + manueller Trigger, Then enthält der erstellte Outlook-Draft eine CSV mit 3 Zeilen (Status: 1× bestätigt, 1× abweichung_betrag, 1× keine_unterschrift) | should |
Testplan
| ID | AC | Art | Automatisiert | Repo-Pfad | Status |
|---|---|---|---|---|---|
| T1 | 1, 4 | unit | ja | gateway/tests/unit/graphicalEditor/test_workflow_file_io.py |
pending |
| T2 | 2, 3, 10 | api | ja | gateway/tests/integration/graphicalEditor/test_workflow_import.py |
pending |
| T3 | 5, 6 | unit | ja | gateway/tests/unit/serviceAgent/test_workflow_tools_crud.py |
pending |
| T4 | 7, 8, 9, 13 | e2e | manuell | PWG-Demo-Instanz mit 3 Test-Scans (über pwgDemo2026.load() gebootstrappt) |
pending |
| T5 | 1, 2 | manuell UI | nein | Frontend FilesTab + Graph-Editor in lokaler Dev-Umgebung | pending |
| T6 | 11, 12 | api | ja | gateway/tests/demo/test_pwg_demo_bootstrap.py (analog zu test_demo_bootstrap.py) |
pending |
Links
-
Quellplan (0-ideas):
wiki/c-work/0-ideas/2026-04-pm-consolidated-customer-requirements.md(Abschnitt 1.9c) -
PWG-Workshop-Inputs:
pamocreate/projects/poweron/customer-pwg/20260415-inputs-pwg.txt -
Graph-Editor Models:
gateway/modules/features/graphicalEditor/datamodelFeatureGraphicalEditor.py -
Graph-Editor Routes:
gateway/modules/features/graphicalEditor/routeFeatureGraphicalEditor.py -
System-Templates Bootstrap:
gateway/modules/interfaces/interfaceBootstrap.py(_buildSystemTemplates) -
Agent Workflow-Tools:
gateway/modules/serviceCenter/services/serviceAgent/workflowTools.py -
Toolbox-Registry:
gateway/modules/serviceCenter/services/serviceAgent/toolboxRegistry.py -
UDB Files-Tab:
frontend_nyla/src/components/UnifiedDataBar/FilesTab.tsx -
File-Upload-Route:
gateway/modules/routes/routeDataFiles.py -
Trustee-Modelle:
gateway/modules/features/trustee/datamodelFeatureTrustee.py -
Abacus-Connector:
gateway/modules/features/trustee/accounting/connectors/accountingConnectorAbacus.py -
Feature-Sub-Agent:
gateway/modules/serviceCenter/services/serviceAgent/featureDataAgent.py -
Demo-Config-Basis:
gateway/modules/demoConfigs/_baseDemoConfig.py -
Investor-Demo-Vorlage:
gateway/modules/demoConfigs/investorDemo2026.py -
Demo-Config Auto-Discovery:
gateway/modules/demoConfigs/__init__.py -
Demo-Config-Routen:
gateway/modules/routes/routeAdminDemoConfig.py -
Bestehende Demo-Tests:
gateway/tests/demo/test_demo_bootstrap.py -
PR: ...
-
Issue: ...
Abschluss
b-reference/gateway/architecture.mdergänzen (Workflow-File-IO Sektion)b-reference/frontend-nyla/architecture.mdergänzen (FilesTab Workflow-Handling)TOPICS.mdergänzen (neues Thema "Workflow Portability")- PWG-Pilot-Workflow-Datei in
gateway/demoData/workflows/committed und im Demo-Config (pwgDemo2026.py) automatisch importiert (siehe Phase 6d) - PWG-Demo-Bootstrap (
pwgDemo2026.py) erscheint in_getAvailableDemoConfigs()und ist über die Admin-UI /routeAdminDemoConfigaufrufbar - Dieses Dokument →
c-work/2-build/verschieben sobald Phase 1 startet