28 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.
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). - 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 |
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
- Schema-Modul
_workflowFileSchema.pymitWORKFLOW_FILE_SCHEMA_VERSION = "1.0", Funktionen_validateFileEnvelope(),_normalizeNodePositions(),_stripPersistenceFields(),_buildFileFromWorkflow(). - Interface-Methoden in
interfaceFeatureGraphicalEditor.py:importWorkflowFromDict(envelope, mandateId, featureInstanceId, mode, targetWorkflowId?) -> workflowIdexportWorkflowToDict(workflowId) -> envelope
- Routen in
routeFeatureGraphicalEditor.py:POST /{instanceId}/workflows/import(Body:fileId,mode,targetWorkflowId?).GET /{instanceId}/workflows/{workflowId}/export(Query:asFileId,folderId?).
- Validierungen:
- Bekannte Schema-Version, Pflichtfelder, Node-Typen vorhanden in
STATIC_NODE_TYPES. - Bei Import:
active=falseerzwingen.
- Bekannte Schema-Version, Pflichtfelder, Node-Typen vorhanden in
- Unit-Tests: Round-Trip (Export → Import → erneuter Export, Bytes identisch nach Normalisierung).
Phase 2 — UDB-Erkennung & Frontend-IO
FilesTab.tsx:- Workflow-File-Detection (Endung + Content-Sniffing über
useFiles.isJsonContent). - Workflow-Icon, Context-Menu-Eintrag "In Graph-Editor laden" → öffnet Modal.
- Workflow-File-Detection (Endung + Content-Sniffing über
- Modal "Workflow importieren": Editor-Instanz-Auswahl, Mode-Wahl (Neu / Bestehenden ersetzen), Submit → API.
- Graph-Editor-Toolbar: Buttons "Importieren" (Files-Picker) und "Exportieren" (Download-vs-UDB-Modal).
api/workflowApi.ts(oder analog):importWorkflowFromFile(fileId, …),exportWorkflowToFile(workflowId, …).
Phase 3 — Agent-Tools Full-CRUD
- In
workflowTools.pyneue Funktionen:_createWorkflow,_createWorkflowFromFile,_exportWorkflowToFile,_deleteWorkflow(alle mit_-Prefix gemäss Naming-Konvention). - Tool-Definitionen in
getWorkflowToolDefinitions()ergänzen. toolboxRegistry.py— Tool-Liste inworkflow-Toolbox erweitern.- Sicherheits-Confirm:
deleteWorkflowverlangtconfirm: true. - Integration-Test: Agent erstellt Workflow → exportiert → löscht → re-importiert via fileId.
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:
- Definition in
gateway/modules/features/graphicalEditor/nodeDefinitions/trustee.pyergänzen (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(Debitor-Filter), dann Aggregat inTrusteeDataJournalLineüber die für Mietzins relevanten Konten in der Periode. - Mode
raw→ direkterFeatureDataProvider.queryTablemitentityals Tabellenname. - Mode
aggregate→FeatureDataProvider.aggregateTablemitextraFilter. - Output-Format:
{ matched: bool, contacts: [...], journalLines: [...], expectedRentAmount: number|null, matchConfidence: number }— direkt verwendbar alscontextinai.prompt.
- Mode
- Registrierung in
methodTrustee/__init__.pyundmethodTrustee.py(analog zu existierenden Actions). - Unit-Test
tests/methods/methodTrustee/test_queryData.py: MockFeatureDataProvider, prüfe Match-Logik mit Tippfehler-Toleranz und Mietzins-Aggregation.
Sub-Task 4b — Attachment-Support in email.draftEmail:
- In
nodeDefinitions/email.pyneuen optionalen Parameter ergänzen:{"name": "attachments", "type": "json", "required": False, "frontendType": "attachmentBuilder", "description": t("Anhänge (Liste von { name, contentRef | csvFromVariable | base64Content })"), "default": []}, - In
gateway/modules/workflows/methods/methodOutlook/actions/composeAndDraftEmailWithContext.py(oder dem aktuellen Pfad) Attachment-Handling implementieren:contentRef→ resolved aus Wire/Variable, als Bytes/String.csvFromVariable→ resolved aus Variable (z. B. Output vondata.consolidate) und als CSV-Anhang gehängt.base64Content→ direkt dekodieren.- Hochladen als Outlook-Draft-Attachment via Graph-API (
/me/messages/{id}/attachments).
- Frontend: Attachment-Builder im Property-Panel des Editors (kann minimal sein — JSON-Editor reicht für Pilot).
- Unit-Test mit gemocktem Outlook-Connector: prüfe dass Attachment korrekt im erstellten Draft enthalten ist.
Sub-Task 4c — Frontend-Anpassungen für neue Nodes:
trustee.queryDataautomatisch in der Node-Palette sichtbar (kommt durch Definition gratis).- Property-Panel: Bei
mode: "lookup"undentity: "tenantWithRent"die Tenant-Felder anzeigen, sonst ausblenden (dependsOn-Mechanismus wie insharepointFolder). - Falls beide Wege zu aufwändig: Pilot-Workflow auf 7 Nodes vereinfachen — Step 5 macht ALLES (Sub-Agent-Query + Klassifikation + Antwort) in einem
ai.prompt-Node mitfeatureSubAgent-Tool-Zugriff.
Phase 5 — Pilot-Workflow als File
- Datei
gateway/demoData/workflows/pwg-mietzinsbestaetigung-pilot.workflow.jsonerstellen (Inhalt wie oben spezifiziert). - Datei via Test in eine PWG-Demo-Instanz importieren (manueller Smoke-Test).
- Test-Lauf mit 2–3 fiktiven Scan-PDFs (in
gateway/demoData/pwg/scans/ablegen — Sub-Aufgabe). - CSV-Ergebnis prüfen: enthält pro Scan eine Zeile mit allen Feldern aus dem Output-Schema.
Querschnitt
- API-Endpunkte: ja, 2 neue (Import + Export).
- DB-Schema / Migration: nein.
- Frontend-Komponenten: FilesTab-Erweiterung, Graph-Editor-Toolbar-Buttons, Import-Modal.
- RBAC / Permissions: Import/Export benötigen gleiche Rechte wie
update_workflow.deleteWorkflow-Tool nur wenn User-Rolledelete_workflowdarf. - Neutralisierung betroffen? Indirekt ja: Workflow-Files können sensible Parameter (Connection-Refs, E-Mail-Adressen) enthalten. Doku-Hinweis beim Export-Modal: "Vor Weitergabe Datei prüfen". Keine automatische Neutralisierung im Pilot-Scope.
- Navigation / Routing: keine Änderung.
- 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 Tokensals Grössenordnung — gehört in PWG-Preismodell-Action-Item AI3.
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 |
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 | e2e | manuell | PWG-Demo-Instanz mit 3 Test-Scans | pending |
| T5 | 1, 2 | manuell UI | nein | Frontend FilesTab + Graph-Editor in lokaler Dev-Umgebung | 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 -
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) referenziert - Dieses Dokument →
c-work/2-build/verschieben sobald Phase 1 startet