wiki/c-work/1-plan/2026-04-pwg-pilot-mietzinsbestaetigung-workflow.md
2026-04-20 00:31:02 +02:00

40 KiB
Raw Blame History

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:

  1. 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.
  2. Agent-Tools Full-CRUD — Der AI-Agent erhält im workflow-Toolbox die vollständigen Operationen createWorkflow, createWorkflowFromFile, exportWorkflowToFile, deleteWorkflow (zusätzlich zu den bestehenden Lese-/Edit-Tools).
  3. Pilot-Workflow als File — Konkrete Lieferung: Workflow-JSON nach Schema, Step-5 AI-Prompt, Datenextraktion aus Trustee-DB.
  4. PWG-Demo-Bootstrap — Eigenes Demo-Config-Modul pwgDemo2026.py (analog zu investorDemo2026.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 / aggregateTable auf TrusteeDataContact + 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-Tool addNode schreibt position: {x, y}. Beim Import muss normalisiert werden.
  • Workflow-Envelope-Felder: AutoWorkflow hat 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 invocations aktivieren. active wird beim Import auf false gesetzt.
  • Node-Type-Validierung: Beim Import gegen STATIC_NODE_TYPES validieren. 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.json liegt 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 Routen POST .../workflows/import und GET .../workflows/{id}/export.
    • features/graphicalEditor/interfaceFeatureGraphicalEditor.py — neue Methoden importWorkflowFromDict, exportWorkflowToDict.
    • features/graphicalEditor/_workflowFileSchema.py (NEU) — Schema-Definition + Validierung + Normalisierung (x/yposition).
    • serviceCenter/services/serviceAgent/workflowTools.py — neue Tools createWorkflow, createWorkflowFromFile, exportWorkflowToFile, deleteWorkflow.
    • serviceCenter/services/serviceAgent/toolboxRegistry.py — Tool-Liste in workflow-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) und gateway/demoData/pwg/_seedTrusteeData.json (NEU, fiktive Mieter + Mietzins-Buchungen).
  • Andere: Step-5-Prompt-Template als Konstante in gateway/modules/features/trustee/promptTemplates/_pwgMietzinsCheck.py oder 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.json oder 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 / updateGraph fü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):

  1. trustee.queryData — neuer Node in nodeDefinitions/trustee.py + Action in methodTrustee/actions/queryData.py. Wrappt FeatureDataProvider.queryTable / aggregateTable. Mode lookup mit Entity tenantWithRent macht intern: Match in TrusteeDataContact (Debitor) → Aggregat über TrusteeDataJournalLine für Mietzins-Konten in der Periode.
  2. email.draftEmail.attachments — optionaler Parameter attachments in nodeDefinitions/email.py ergänzen + im Executor composeAndDraftEmailWithContext Attachment-Handling implementieren.

Datenfluss-Anmerkungen:

  • Der context-Parameter von ai.prompt muss VOR dem Node befüllt werden (verifiziert: methodAi/actions/process.py macht direkten AiCallRequest ohne Sub-Agent-Tools). Daher liegt n6 zwingend vor n7 im Wire-Pfad.
  • data.aggregate (mode collect) sammelt im Loop-Body, data.consolidate (mode csvJoin) führt nach dem Loop zusammen — Standardmuster für CSV-Output aus Loop-Iterationen.
  • helpers/csvProcessing.py (in methodAi) kann von data.consolidate und/oder email.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.pyWORKFLOW_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: envelope ODER fileId, optional existingWorkflowId).
    • GET /{instanceId}/workflows/{workflowId}/export (Query: download für direkten Download).
  • Validierungen:
    • Schema-Version Whitelist (1.0), Pflichtfelder (label, graph.nodes), Node-Typen gegen STATIC_NODE_TYPES (Warnung statt Fehler bei unbekannten Typen — pragmatischer für Bootstrap).
    • Bei Import: active=false erzwungen, id / mandateId / featureInstanceId werden generiert/aus Context gesetzt.
  • 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.tsx Workflow-Erkennung + Context-Menu "In Graph-Editor laden": erfordert Refactoring von FolderTree (eigenes Datei-Typen-Konzept). Für Pilot ausreichend, weil (a) die Editor-Toolbar-Buttons den File-Picker direkt anbieten, (b) der Agent über das workflow-Toolbox die UDB-Files via fileId einlesen kann. Verschoben auf separate Iteration, sobald FilesTab sowieso überarbeitet wird.

Phase 3 — Agent-Tools Full-CRUD DONE (2026-04-19)

  • In workflowTools.py neue 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.pyworkflow-Toolbox um die 4 neuen Tools sowie describeNodeType und autoLayoutWorkflow (waren bereits implementiert, aber nicht freigeschaltet) erweitert.
  • Sicherheits-Confirm: deleteWorkflow verlangt explizit confirm: 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 (mode collect) und data.consolidate (mode csvJoin) existieren bereits → CSV-Sammlung ist abgedeckt, kein neuer Daten-Node nötig.
  • Kein Node liest Trustee-DB-Daten → trustee.queryData muss neu gebaut werden.
  • email.draftEmail hat keinen attachments-Parameter → muss erweitert werden.
  • ai.prompt mit outputFormat: "json" und context-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.py ergänzt (Pattern wie trustee.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".
  • Action-Implementierung gateway/modules/workflows/methods/methodTrustee/actions/queryData.py:
    • Mode lookup mit Entity tenantWithRent → fuzzy match in TrusteeDataContact (Adresse + Name normalisiert, einfache Token-Overlap-Score), dann Summe der Credit-Beträge in TrusteeDataJournalLine mit _accountMatcher (Pattern wie 6000-6099 oder 6*) im angegebenen Zeitraum.
    • Mode raw → liest direkt aus den 5 Trustee-Tabellen (Filter via filterJson).
    • Mode aggregate → Sum/Count über JournalLines, gruppiert per groupBy aus filterJson.
    • Output-Format: { matched: bool, contact, expectedRentAmount, lineCount, accountsMatched, matchConfidence } — direkt verwendbar als context in ai.prompt.
  • Registrierung in methodTrustee.py (Action-Definition + Methodenbindung); __init__.py brauchte keine Änderung (nutzt direkten Import).
  • Unit-Test tests/unit/workflow/test_trusteeQueryData.py prü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.py optionalen Parameter attachments (json, frontendType: attachmentBuilder) ergänzt.
  • In gateway/modules/workflows/methods/methodOutlook/actions/composeAndDraftEmailWithContext.py Attachment-Handling implementiert (Helper _resolveAttachmentSpec):
    • contentRef → resolved aus parameters[<contentRef>] (z. B. CSV vom data.consolidate-Node), als String.
    • csvFromVariable → Shorthand für CSV-Ref, hängt .csv an, MIME text/csv.
    • base64Content → direkt verwendet, MIME aus mimeType oder Default.
    • Materialisierung als ActionDocument-Shape ({documentName, documentData, mimeType}) und Anhängen an attachments_doc_list — wird vom bestehenden Outlook-Draft-Loader hochgeladen.
  • Frontend Attachment-Builder UI: NICHT NÖTIG — der attachments-Parameter wird programmatisch via csvFromVariable: "csv" aus dem data.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.queryData automatisch in der Node-Palette sichtbar (kommt durch Definition gratis).
  • Property-Panel: Sichtbarkeit über frontendOptions.dependsOn + showWhen deklariert (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 mit featureSubAgent-Tool-Zugriff. Aktuell nicht aktiviert — der Workflow läuft mit dediziertem trustee.queryData-Node.

Phase 5 — Pilot-Workflow als File DONE (2026-04-19)

  • Datei gateway/demoData/workflows/pwg-mietzinsbestaetigung-pilot.workflow.json erstellt — 10 Nodes (trigger.manualsharepoint.listFilesflow.loopsharepoint.downloadFiletrustee.extractFromFilestrustee.queryDataai.promptdata.aggregatedata.consolidateemail.draftEmail), AI-Prompt für Klassifikation (bestaetigt / abweichung_betrag / keine_unterschrift / mieterwechsel / unklar), CSV-Attachment via csvFromVariable: "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.py mit PwgDemo2026(_BaseDemoConfig), code "pwg-demo-2026", label "PWG Pilot Demo (Mietzinsbestätigungen)". load() / remove() analog investorDemo2026.py (Helpers wurden vorerst kopiert; Refactor in _demoHelpers.py ist eigene Iteration).
  • 6b — Mandant + User + Features: Mandant stiftung-pwg, User pwg.demo@poweron.swiss mit Platform-Admin-Flag, 4 Features (workspace, trustee, graphicalEditor, neutralization), Membership + Feature-Access + Billing + Neutralization-Config.
  • 6c — Trustee-Seed-Daten: gateway/demoData/pwg/_seedTrusteeData.json mit 5 Mieter-Contacts und je 12 monatlichen JournalLines für 2026. Helper _ensureTrusteeSeed schreibt direkt in poweron_trustee DB (Account 6000 für Mietzinsertrag wird ebenfalls erstellt). Idempotent über external_id.
  • 6d — Pilot-Workflow Auto-Import: _ensurePilotWorkflow lädt pwg-mietzinsbestaetigung-pilot.workflow.json, ersetzt Platzhalter <<TRUSTEE_INSTANCE_ID>> mit der real angelegten Trustee-Instanz, importiert via importWorkflowFromDict (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 zu trigger.manual mit 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 → Login pwg.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, _validateInstanceAccess prüft Membership + Feature-Access. Zusätzlich confirm: true-Flag-Schutz im Agent-Tool deleteWorkflow. Kein eigener Permission-Layer nötig (Templates haben bereits templateScope + sharedReadOnly fü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)

  1. Login als Platform-Admin.
  2. Sidebar oben rechts: Avatar → „Admin" → Reiter „Demo-Konfigurationen".
  3. Eintrag „PWG Pilot Demo (Mietzinsbestätigungen)" suchen → Klick „Laden".
  4. 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)

  1. Logout, Login als pwg.demo@poweron.swiss (Default-Passwort siehe Modal aus Test A).
  2. Mandanten-Wechsler oben links → „Stiftung PWG" auswählen.
  3. Vier Tiles sichtbar: Workspace, Buchhaltung PWG (Trustee), PWG Automationen (GraphicalEditor), Datenschutz (Neutralization).
  4. Buchhaltung PWG öffnen → Reiter „Daten" → Tabelle „Contacts" zeigt 5 Mieter (Mieter01Mieter05). Tabelle „JournalLines" zeigt 60 Einträge (Filter account = 6000).
  5. 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:

  1. In der Workflow-Liste Action „Als Datei exportieren" an der Pilot-Zeile klicken → Browser lädt pwg-jahresmietzinsbestaetigung-pilot.workflow.json herunter.
  2. Header-Button „Importieren" klicken → eben heruntergeladene Datei auswählen.
  3. Toast „Workflow importiert (deaktiviert)" erscheint, Liste enthält jetzt zwei Workflows (zweiter mit identischem Label, ebenfalls inactive).
  4. Neuen Workflow per Action löschen.

Teil 2 — Agent-CRUD:

  1. Pilot-Workflow öffnen → KI-Tab unten rechts.
  2. Prompt: „Erstelle einen leeren Workflow namens 'Smoke-Test'." → Agent-Antwort enthält neue Workflow-ID, Liste aktualisiert sich.
  3. Prompt: „Lösche den Workflow 'Smoke-Test'." → Agent fragt nach Bestätigung (confirm flag missing).
  4. Prompt: „Ja, bitte löschen, mit confirm true." → Workflow ist weg.

Test D — End-to-End mit Outlook (≈ 5 min, braucht Outlook-Account)

  1. Pilot-Workflow öffnen, Editor-Canvas sichtbar.
  2. Property-Panel der Nodes prüfen:
    • sharepoint.listFiles Source-Folder auf einen erreichbaren SharePoint-/OneDrive-Ordner setzen.
    • email.draftEmail Connection auf den eigenen Outlook-Account, Empfänger auf eigene Adresse setzen (Demo-Mail an sich selbst).
  3. Test-PDFs vorbereiten: python gateway/demoData/pwg/_generateScans.py einmal ausführen → 3 PDFs in gateway/demoData/pwg/scans/. Diese in den oben gewählten SharePoint-Ordner hochladen.
  4. Workflow oben rechts auf „Aktiv" schalten → Button „Manuell ausführen" klicken.
  5. Run-Detail-Seite zeigt Step-by-Step-Logs:
    • 3 Loop-Durchläufe (1 pro PDF).
    • In jedem Loop: trustee.extractFromFilestrustee.queryData (matched=true) → ai.prompt (status gefüllt).
    • Am Ende: data.consolidate produziert CSV mit 3 Zeilen, email.draftEmail erstellt Draft.
  6. 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)

  1. Logout, wieder als Platform-Admin einloggen.
  2. Admin → Demo-Konfigurationen → „PWG Pilot Demo" → „Entfernen".
  3. Toast „Demo entfernt". Mandant „Stiftung PWG" verschwindet aus der Liste.
  4. 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 exportWorkflowToFilecreateWorkflowFromFile, 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
  • 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.md ergänzen (Workflow-File-IO Sektion)
  • b-reference/frontend-nyla/architecture.md ergänzen (FilesTab Workflow-Handling)
  • TOPICS.md ergä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 / routeAdminDemoConfig aufrufbar
  • Dieses Dokument → c-work/2-build/ verschieben sobald Phase 1 startet