From c9454a618fb04d5c9f4aa1236f011c3de42df64e Mon Sep 17 00:00:00 2001
From: ValueOn AG
Date: Thu, 16 Apr 2026 23:12:56 +0200
Subject: [PATCH] feat db-clean-ui and unified content udm
---
TOPICS.md | 6 +-
b-reference/gateway/ai-agent.md | 7 +-
b-reference/gateway/workflow.md | 10 +-
b-reference/platform/database-architecture.md | 87 ++-
b-reference/platform/neutralization.md | 22 +-
...6-04-consolidated-customer-requirements.md | 572 +++++++++++++++
...6-04-demo2-merged-customer-trustee-plan.md | 0
...6-04-porta-ui-enhancements-team-meeting.md | 0
e-compliance/neutralisierung-detail.md | 8 +-
...026-04-database-health-and-data-cleanup.md | 46 +-
z-archive/2026-04-unified-document-model.md | 685 ++++++++++++++++++
z-archive/unified-document-model.md | 511 +++++++++++++
12 files changed, 1908 insertions(+), 46 deletions(-)
create mode 100644 c-work/1-plan/2026-04-consolidated-customer-requirements.md
rename c-work/{1-plan => 4-done}/2026-04-demo2-merged-customer-trustee-plan.md (100%)
rename c-work/{1-plan => 4-done}/2026-04-porta-ui-enhancements-team-meeting.md (100%)
rename {c-work/1-plan => z-archive}/2026-04-database-health-and-data-cleanup.md (87%)
create mode 100644 z-archive/2026-04-unified-document-model.md
create mode 100644 z-archive/unified-document-model.md
diff --git a/TOPICS.md b/TOPICS.md
index 87e647f..0570598 100644
--- a/TOPICS.md
+++ b/TOPICS.md
@@ -1,5 +1,5 @@
-
+
# Themen-Index für AI-Kontext
@@ -33,7 +33,7 @@ Lade immer zuerst diese Datei. Dann gezielt die passende(n) Referenz-Datei(en).
|-------|-------|------------|
| Neutralisierung | b-reference/platform/neutralization.md | Datenschutz, AI-Call-Pipeline, Failsafe |
| RBAC | b-reference/platform/rbac.md | 4-Stufen-Modell, Template-Rollen, Resolution, Datenmodell |
-| Datenbank-Architektur | b-reference/platform/database-architecture.md | Interface-Pattern, Connector, Auto-Init, DB-Liste |
+| Datenbank-Architektur | b-reference/platform/database-architecture.md | Interface-Pattern, Connector, Auto-Init, DB-Liste, Database Health, Orphan-Scanner |
| Navigation | b-reference/platform/navigation.md | Menü-Struktur, Admin-Seiten, API |
| Compliance & AI-Audit | b-reference/platform/audit.md | AI-Datenfluss-Log, Security-Audit, Statistiken, RBAC |
| i18n / Mehrsprachigkeit | b-reference/gateway/architecture.md (Abschnitt i18n), d-guides/coding-conventions.md (Backend i18n), b-reference/frontend-nyla/architecture.md (Routing/i18n) | `t()`, `@i18nModel`, UiLanguageSet, TextMultilingual, AI-Uebersetzung, Boot-Sync |
@@ -50,7 +50,9 @@ Lade immer zuerst diese Datei. Dann gezielt die passende(n) Referenz-Datei(en).
| Gateway i18n Phase 7 (done) | c-work/3-validate/2026-04-gateway-i18n-phase-7-implementation.md | RBAC-Keys (rbac.*) im xx-Set, `translate-field` API, FormGenerator KI-Button |
| Gateway Duplicate Class Names (done) | c-work/3-validate/2026-04-gateway-duplicate-class-names.md | TaskResult, AiResponse, TableData, Token Umbenennungen |
| Generic Graph Editor (Typed Nodes, done) | c-work/3-validate/2026-04-generic-graph-editor.md | Port-Typen, Extraktoren, FrontendType-Renderer, System-Variablen |
+| Unified Document Model (UDM) & Workflows (done) | z-archive/2026-04-unified-document-model.md, b-reference/gateway/workflow.md (Abschnitt UDM) | UDM-Baum, Extract-Node, Loop/Consolidate, Agent-UDM-Tools |
| i18n Static Text Elimination (Ph. 1–2 done) | c-work/2-build/2026-04-i18n-static-text-elimination.md | Gateway Feature+Nodes: Dicts → de-Keys; Ph. 3–5 offen |
+| Database Health & Data Cleanup (done) | z-archive/2026-04-database-health-and-data-cleanup.md | DB-Registry, FK-Discovery, Orphan-Scanner, Admin-Seite |
## Prozess & Betrieb
diff --git a/b-reference/gateway/ai-agent.md b/b-reference/gateway/ai-agent.md
index c1140e9..2a56c28 100644
--- a/b-reference/gateway/ai-agent.md
+++ b/b-reference/gateway/ai-agent.md
@@ -1,5 +1,5 @@
-
+
# AI Agent & Knowledge Store
@@ -92,7 +92,7 @@ Zusätzlich zu den unten genannten **Kern-Tools** existieren **dynamische Tools*
**Toolbox-Zuordnung:** Kern-Tools sind den Toolboxes `core`, `ai` und `datasources` zugeordnet (siehe Toolbox Registry oben). Connection-abhaengige Tools (`email`, `sharepoint`, `clickup`, `jira`) werden nur aktiviert, wenn der User eine passende Connection hat. Workflow-Editing-Tools (`workflow` Toolbox) werden separat via `workflowTools.py` registriert.
-### Kern-Tools (registriert via `registerCoreTools` → `coreTools/`, 40 Stück)
+### Kern-Tools (registriert via `registerCoreTools` → `coreTools/`; Stand 2026-04 inkl. UDM-Helfer)
**Workspace / Dateien**
@@ -149,6 +149,9 @@ Zusätzlich zu den unten genannten **Kern-Tools** existieren **dynamische Tools*
| `readContentObjects` | Gezielt Content-Objekte lesen |
| `extractContainerItem` | Element aus Container extrahieren |
| `summarizeContent` | KI-Zusammenfassung |
+| `getUdmStructure` | UDM-JSON: Überblick (Knoten, Struktur, Block-Zahlen); `udmJson` als stringifiziertes Objekt |
+| `walkUdmBlocks` | UDM traversieren: alle `ContentBlock`-Knoten mit Pfad und Kurz-Preview |
+| `filterUdmByType` | UDM: alle Blöcke mit gegebenem `contentType` (z. B. `table`, `image`) |
| `describeImage` | Vision-Analyse |
| `renderDocument` | Dokument rendern |
| `generateImage` | Bildgenerierung |
diff --git a/b-reference/gateway/workflow.md b/b-reference/gateway/workflow.md
index 7eaf788..fdb3571 100644
--- a/b-reference/gateway/workflow.md
+++ b/b-reference/gateway/workflow.md
@@ -1,5 +1,5 @@
-
+
# Workflow-Engine
@@ -50,7 +50,7 @@ Methoden sind Python-Klassen unter `gateway/modules/workflows/methods/`; der **M
| Method (`self.name`) | Actions (Registry-Keys) |
|---------------------|-------------------------|
| `context` | `getDocumentIndex`, `extractContent`, `neutralizeData`, `triggerPreprocessingServer` |
-| `ai` | `process`, `webResearch`, `summarizeDocument`, `translateDocument`, `convertDocument`, `generateDocument`, `generateCode` |
+| `ai` | `process`, `webResearch`, `summarizeDocument`, `translateDocument`, `convertDocument`, `generateDocument`, `generateCode`, `consolidate` (KI-Konsolidierung aggregierter Ergebnisse) |
| `outlook` | `readEmails`, `searchEmails`, `composeAndDraftEmailWithContext`, `sendDraftEmail` |
| `sharepoint` | `findDocumentPath`, `readDocuments`, `uploadDocument`, `listDocuments`, `analyzeFolderUsage`, `findSiteByUrl`, `downloadFileByPath`, `copyFile`, `uploadFile` |
| `clickup` | `listTasks`, `searchTasks`, `getTask`, `createTask`, `updateTask`, `uploadAttachment` |
@@ -61,6 +61,12 @@ Methoden sind Python-Klassen unter `gateway/modules/workflows/methods/`; der **M
*Hinweis:* Ältere Kontext-Dokumente nennen für `context` u. a. `saveContent` / `transformContent`; der aktuelle Gateway-Stand listet die obigen Aktionen (Stand Abgleich Code / Review 2026-04-05).
+### Graphical Editor — UDM, Loop, KI-Badge
+
+- **Unified Document Model (UDM):** Extraktion kann ein hierarchisches JSON (`Document` → `StructuralNode` → `ContentBlock`) liefern; Port-Typen `UdmDocument`, `UdmNodeList`, `ConsolidateResult` im Typed-Port-System (`portTypes.py`).
+- **Nodes:** u. a. `context.extractContent` (ohne KI), `data.consolidate` (deterministisch), `ai.consolidate` (LLM), `flow.loop` mit Parametern `level` (UDM-Ebene) und `concurrency` (parallele Iterationen).
+- **Kosten-Transparenz:** Jede Node-Definition trägt `meta.usesAi` (`true` | `false`). Das Frontend (`FlowCanvas`, Node-Palette) zeigt ein **AI-Badge** nur bei `usesAi: true`.
+
---
## Aktionen
diff --git a/b-reference/platform/database-architecture.md b/b-reference/platform/database-architecture.md
index a08a837..1a02fdf 100644
--- a/b-reference/platform/database-architecture.md
+++ b/b-reference/platform/database-architecture.md
@@ -239,16 +239,84 @@ Felder mit fuehrendem `_` sind fuer Anwendungs-CUD geschuetzt -- der Connector e
## Feature-DB-Registrierung
-Features registrieren sich **nicht** in einer zentralen DB-Liste. Stattdessen:
+Jedes Interface registriert seine Datenbank ueber `registerDatabase()` aus `dbRegistry.py`:
-1. `registry.py` → `registerAllFeaturesInCatalog()` laedt Feature-Main-Module
-2. Jedes Feature-Main definiert seinen `FEATURE_CODE` und `dbDatabase`
-3. Der Datenbank-Name wird in `_initializeDatabase()` des Feature-Interfaces gesetzt
-4. Der `DatabaseConnector` erzeugt die DB automatisch beim ersten Zugriff
+1. Jedes `interfaceDb*.py` / `interfaceFeature*.py` definiert eine Modul-Konstante (z.B. `appDatabase = "poweron_app"`)
+2. Auf Modul-Ebene wird `registerDatabase(appDatabase)` aufgerufen — damit ist die DB im zentralen Registry
+3. Der `DatabaseConnector` erzeugt die DB automatisch beim ersten Zugriff
+4. Neue DBs werden automatisch erkannt, entfernte DBs verschwinden
-### Admin-Sicht
+---
-Die Admin-DB-Listing-API (`routeSecurityAdmin.py`) wurde am 2026-04-12 entfernt. Datenbank-Diagnostik erfolgt direkt ueber PostgreSQL-Tools oder das System-Dashboard.
+## Database Health und Orphan-Scanner
+
+### Ueberblick
+
+SysAdmin-Seite unter **Admin > System > Datenbank-Gesundheit** mit zwei Funktionen:
+
+1. **Tabellenstatistiken** — Row Count, Size, Index Size, Last Vacuum/Analyze fuer alle Tabellen
+2. **Orphan Cleanup** — Generische Erkennung verwaister Datensaetze mit Clean-Buttons
+
+### Dynamische DB-Registry (`modules/shared/dbRegistry.py`)
+
+- `registerDatabase(dbName, configPrefix)` — oeffentliche API, aufgerufen in jedem Interface
+- `_getRegisteredDatabases()` — alle registrierten DBs
+- `_getConnectorForDb(dbName)` — Factory fuer read-only Connector
+
+### Model-Registry (`modules/datamodels/datamodelBase.py`)
+
+- `_MODEL_REGISTRY: Dict[str, Type[PowerOnModel]]` — automatisch via `__init_subclass__`
+- Jede `PowerOnModel`-Subklasse registriert sich beim Import (Tabellenname = Klassenname)
+
+### FK-Discovery (`modules/shared/fkRegistry.py`)
+
+- Scannt alle PowerOnModel-Subklassen nach `fk_target` in `json_schema_extra`
+- Baut automatisch `{tableName → dbName}` Mapping aus den Annotationen
+- Fallback: Catalog-Query (`information_schema.tables`) fuer unmapped Tables
+- Cached nach erstem Scan
+- `FkRelationship` Dataclass: `sourceDb, sourceTable, sourceColumn, targetDb, targetTable, targetColumn`
+
+### FK-Annotationen (`fk_target`)
+
+Jedes `*Id`-Feld das eine echte FK-Beziehung darstellt, hat `fk_target` in `json_schema_extra`:
+
+```python
+mandateId: str = Field(
+ ...,
+ json_schema_extra={
+ ...,
+ "fk_target": {"db": "poweron_app", "table": "Mandate"},
+ },
+)
+```
+
+- Standard `targetColumn` ist `"id"`, Sonderfall `Feature.code` nutzt `"column": "code"`
+- Felder ohne DB-FK (Stripe-IDs, Graph-Node-IDs, polymorphe referenceId) haben kein `fk_target`
+- `fk_target` ist rein fuer Backend-Orphan-Detection, `fk_model`/`frontend_fk_*` bleiben fuer das UI
+
+### Orphan-Scanner (`modules/system/databaseHealth.py`)
+
+- `_getTableStats(dbFilter)` — `pg_stat_user_tables` + `pg_total_relation_size`
+- `_scanOrphans(dbFilter)` — Same-DB: `NOT EXISTS`, Cross-DB: Parent-IDs laden + `NOT IN`
+- `_cleanOrphans(db, table, column)` — loescht Orphans, gibt Count zurueck
+- `_cleanAllOrphans()` — alle Orphans bereinigen
+- 5-Minuten-Cache fuer Orphan-Ergebnisse
+
+### API (`modules/routes/routeAdminDatabaseHealth.py`)
+
+| Methode | Pfad | Beschreibung |
+|---------|------|-------------|
+| GET | `/api/admin/database-health/stats` | Tabellenstatistiken (optional `?db=...`) |
+| GET | `/api/admin/database-health/orphans` | Orphan-Scan (optional `?db=...`) |
+| POST | `/api/admin/database-health/orphans/clean` | Einzeln-Cleanup `{"db","table","column"}` |
+| POST | `/api/admin/database-health/orphans/clean-all` | Batch-Cleanup aller Orphans |
+
+Alle Endpunkte: SysAdmin-only via `requireSysAdminRole`.
+
+### Frontend (`AdminDatabaseHealthPage.tsx`)
+
+- Tab "Statistiken": Sortierbare Tabelle mit DB-Filter und Summary-Leiste
+- Tab "Orphan Cleanup": Tabelle mit Clean-Button pro Zeile + "Alle bereinigen"
---
@@ -257,6 +325,10 @@ Die Admin-DB-Listing-API (`routeSecurityAdmin.py`) wurde am 2026-04-12 entfernt.
| Thema | Pfad |
|-------|------|
| PostgreSQL Connector | `gateway/modules/connectors/connectorDbPostgre.py` |
+| DB-Registry | `gateway/modules/shared/dbRegistry.py` |
+| FK-Registry | `gateway/modules/shared/fkRegistry.py` |
+| Database Health | `gateway/modules/system/databaseHealth.py` |
+| Health API Route | `gateway/modules/routes/routeAdminDatabaseHealth.py` |
| App-DB Interface | `gateway/modules/interfaces/interfaceDbApp.py` |
| Chat-DB Interface | `gateway/modules/interfaces/interfaceDbChat.py` |
| Management-DB Interface | `gateway/modules/interfaces/interfaceDbManagement.py` |
@@ -268,3 +340,4 @@ Die Admin-DB-Listing-API (`routeSecurityAdmin.py`) wurde am 2026-04-12 entfernt.
| Bootstrap / DB-Seed | `gateway/modules/interfaces/interfaceBootstrap.py` |
| DB-Migration Script | `gateway/scripts/script_db_export_migration.py` |
| Datenmodelle (Pydantic) | `gateway/modules/datamodels/` |
+| Frontend Health Page | `frontend_nyla/src/pages/admin/AdminDatabaseHealthPage.tsx` |
diff --git a/b-reference/platform/neutralization.md b/b-reference/platform/neutralization.md
index 732cdd3..64060e8 100644
--- a/b-reference/platform/neutralization.md
+++ b/b-reference/platform/neutralization.md
@@ -1,6 +1,6 @@
-
-
+
+
# Neutralisierungs-System
@@ -26,7 +26,7 @@ Orthogonal dazu (kein Ersatz des Gates, sondern Data-at-rest bzw. Workflow):
2. **Chat-/Session-Level:** `ServiceCenterContext.requireNeutralization` (z. B. Workspace, Automation).
3. **Pro Request:** `AiCallRequest.requireNeutralization` — expliziter Override auf dem Call.
-**Quellen gesamt (OR-Logik):** Ist irgendwo Neutralisierung erforderlich (Feature-Instanz, Workflow/Session, konkretes Objekt mit `FileItem.neutralize` / `DataSource.neutralize` / `FeatureDataSource.neutralize`), wird entsprechend verarbeitet. Dokumentierte Produktregel: **Kein `False` von einer Quelle hebt ein `True` einer anderen auf.**
+**Quellen gesamt (OR-Logik):** Ist irgendwo Neutralisierung erforderlich (Feature-Instanz, Workflow/Session, konkretes Objekt mit `FileItem.neutralize` / `FileFolder.neutralize` / `DataSource.neutralize` / `FeatureDataSource.neutralize` / `FeatureDataSource.neutralizeFields`), wird entsprechend verarbeitet. Dokumentierte Produktregel: **Kein `False` von einer Quelle hebt ein `True` einer anderen auf.**
## Was neutralisiert wird
@@ -45,11 +45,13 @@ Im zentralen AI-Gate (`_neutralizeRequest`):
## Failsafe-Kette
1. **Toggle `FileItem.neutralize`:** Index und Chunks werden synchron gelöscht; Re-Index im Hintergrund verhindert Leaks bei Fehlern in der Nachindexierung.
-2. **Indexierung:** `indexFile()` neutralisiert Text-Chunks bei `FileItem.neutralize=True` → Data-at-rest abgesichert.
-3. **DataSource-Download:** `neutralize`-Flag wird auf erzeugte `FileItem`s vererbt.
-4. **FeatureDataSource:** `_queryFeatureInstance()` setzt bei `neutralize=True` u. a. `requireNeutralization=True` für Sub-Agent-AI-Calls.
-5. **AI-Call:** Bei erzwungener Neutralisierung (`requireNeutralization=True`) schlägt fehlgeschlagene Neutralisierung hart fehl (`RuntimeError` / Blockierung bzw. Entfernen betroffener Message-Teile — kein Durchleiten im Rohzustand).
-6. **`_rehydrateResponse()`:** Als Hilfsmethode vorhanden; automatische Rückübersetzung der Modellantwort ist nicht mehr der Standardpfad.
+2. **Toggle `FileFolder.neutralize`:** Propagiert `neutralize` rekursiv auf alle Dateien im Ordner; Index/Chunks werden pro Datei synchron gelöscht, Re-Index im Hintergrund. Neue/verschobene Dateien erben das Flag des Ziel-Ordners.
+3. **Indexierung:** `indexFile()` neutralisiert Text-Chunks bei `FileItem.neutralize=True` → Data-at-rest abgesichert.
+4. **DataSource-Download:** `neutralize`-Flag wird auf erzeugte `FileItem`s vererbt.
+5. **FeatureDataSource (Tabellen-Level):** `_queryFeatureInstance()` setzt bei `neutralize=True` u. a. `requireNeutralization=True` für Sub-Agent-AI-Calls.
+6. **FeatureDataSource (Feld-Level):** `neutralizeFields` definiert Spalten, deren Werte in `FeatureDataProvider` mit deterministischen Platzhaltern `[NEUT..]` ersetzt werden, bevor Daten den Sub-Agent erreichen. Gleichheits-Vergleiche bleiben möglich (stabiler Hash).
+7. **AI-Call:** Bei erzwungener Neutralisierung (`requireNeutralization=True`) schlägt fehlgeschlagene Neutralisierung hart fehl (`RuntimeError` / Blockierung bzw. Entfernen betroffener Message-Teile — kein Durchleiten im Rohzustand).
+8. **`_rehydrateResponse()`:** Als Hilfsmethode vorhanden; automatische Rückübersetzung der Modellantwort ist nicht mehr der Standardpfad.
**Engine-Failsafe (Spezifikation):** Neutralisierung verlangt, Engine nicht verfügbar → nicht weiterverarbeiten; Teilfehler → Teil entfernen, nicht roh weitergeben; fehlendes internes Medienmodell → Medien-Part entfernen.
@@ -66,10 +68,12 @@ Im zentralen AI-Gate (`_neutralizeRequest`):
| **Feature-Config / DB** | `features/neutralization/datamodelFeatureNeutralizer.py`, `interfaceFeatureNeutralizer.py` |
| **RAG-Index** | `serviceCenter/services/serviceKnowledge/mainServiceKnowledge.py` |
| **Agent / Quellen** | `serviceCenter/services/serviceAgent/mainServiceAgent.py` (`_resolveDataSource`, `_downloadFromDataSource`, `_queryFeatureInstance`) |
+| **Feld-Neutralisierung (DB)** | `serviceCenter/services/serviceAgent/featureDataProvider.py` — `_neutralizeRowFields`, `_applyFieldNeutralization` |
| **Datei-Toggle / Index** | `routes/routeDataFiles.py` — `PATCH /{fileId}/neutralize` |
+| **Ordner-Toggle / Index** | `routes/routeDataFiles.py` — `PATCH /folders/{folderId}/neutralize` (rekursive Propagierung) |
| **Workflow-Aktion** | `workflows/methods/methodContext/actions/neutralizeData.py` |
| **Kontext** | `serviceCenter/context.py` — `requireNeutralization` |
-| **Flags (Modelle)** | `datamodels/datamodelFiles.py` (`FileItem.neutralize`), `datamodelDataSource.py`, `datamodelFeatureDataSource.py` |
+| **Flags (Modelle)** | `datamodels/datamodelFiles.py` (`FileItem.neutralize`), `datamodelFileFolder.py` (`FileFolder.neutralize`), `datamodelDataSource.py`, `datamodelFeatureDataSource.py` (`neutralize`, `neutralizeFields`) |
| **AI-Operationen / Enums** | `datamodels/datamodelAi.py`, `aicore/aicorePluginPrivateLlm.py` |
## Regeln / Invarianten
diff --git a/c-work/1-plan/2026-04-consolidated-customer-requirements.md b/c-work/1-plan/2026-04-consolidated-customer-requirements.md
new file mode 100644
index 0000000..f62bb0a
--- /dev/null
+++ b/c-work/1-plan/2026-04-consolidated-customer-requirements.md
@@ -0,0 +1,572 @@
+
+
+
+
+
+# Konsolidierter Kundenwünsche-Plan — PORTA Umsetzung
+
+> **Stand:** 16. April 2026 (aktualisiert mit PWG-Workshop-Ergebnissen)
+> **Zweck:** Einheitliche Übersicht aller Kundenwünsche, priorisiert und gegen die Codebase abgeglichen.
+> **Ersetzt:** `local/notes/demo-tue-use-cases-inputs-customers.md`, `c-work/1-plan/2026-04-demo2-merged-customer-trustee-plan.md`, `c-work/1-plan/2026-04-porta-ui-enhancements-team-meeting.md`
+
+---
+
+## Kunden-Übersicht
+
+| Kunde | Branche | Kontakte | Hauptinteresse | Status |
+|-------|---------|----------|----------------|--------|
+| **Bling** | Treuhandbüro | KJS | Belegverarbeitung, Budget, Dashboards, Mandantenmanagement | Trial geplant |
+| **PWG** (Stiftung) | Immobilien/Wohnen (~300 Liegenschaften, 7–8 Pers.) | MB | **Pilot: Jahresmietzinsbestätigungen** (3'200/Jahr), Belegverarbeitung Abacus, CommCoach, Neutralisierung, Knowledge-Retrieval | Workshop 16.04.2026 ✅ — Pilot bestätigt, Versand Sommer 2026 |
+| **Quid / ServiceHunter** | SaaS/Dienstleistung | DC | KPI-Dashboard, Zeiterfassung, Prognosen, Konsolidierung | Follow-up geplant |
+
+---
+
+## Legende Codebase-Status
+
+| Symbol | Bedeutung |
+|--------|-----------|
+| ✅ | Im Code vorhanden und funktional |
+| 🔧 | Grundstruktur vorhanden, Anpassung/Ergänzung nötig |
+| ❌ | Noch nicht umgesetzt |
+| ⏸️ | Bewusst zurückgestellt (wartet auf Input/Entscheid) |
+
+---
+
+## Teil 1: Feature-Anforderungen (Kunden-Use-Cases)
+
+### 1.1 Automatisierte Belegverarbeitung & Spesenverwaltung
+
+**Kunden:** Bling (Prio hoch), PWG, Quid (Spesen)
+
+| # | Anforderung | Codebase-Status | Evidenz / Bemerkung |
+|---|-------------|-----------------|---------------------|
+| 1.1.1 | Spesenbelege per Foto → automatische Klassifikation (Rechnung, Spesenbeleg, Bankauszug) | ✅ | `methodTrustee/actions/extractFromFiles.py` + `processDocuments.py` — Pipeline extrahiert, klassifiziert, verbucht |
+| 1.1.2 | SharePoint-Synchronisation (automatisch, z.B. täglich 22:00) | ✅ | `nodeDefinitions/sharepoint.py` — 6 Nodes; `automation2/scheduleCron.py` für Zeitsteuerung; System-Template "Treuhand: PDF-Klassifizierung & Trustee-Import" in `interfaceBootstrap.py` |
+| 1.1.3 | Automatische Kontierung basierend auf Kontoplan | ✅ | In `processDocuments.py` — AI-gestützte Kontierung gegen Feature-Daten (Kontoplan via Accounting-Bridge) |
+| 1.1.4 | Firmen-Mapping zu Kunden/Lieferanten | 🔧 | Grundstruktur in Accounting-Bridge; kein dediziertes Mapping-UI oder regelbasiertes Matching |
+| 1.1.5 | Optionales Tagging (z.B. "Fuel Station") | 🔧 | Tags auf Dokument-Ebene möglich; kein Beleg-spezifisches Tag-System |
+| 1.1.6 | Buchungsregeln für wiederkehrende Belege | ❌ | Kein regelbasiertes Booking-Template-System |
+| 1.1.7 | Vorsteuer automatisch hinterlegen und auslesen | 🔧 | Accounting-Connectors liefern Steuerdaten; automatische MWST-Zuordnung bei Belegverarbeitung nicht explizit |
+| 1.1.8 | Integration RunMyAccounts | ✅ | `accountingConnectorRma.py` |
+| 1.1.9 | Integration Bexio | ✅ | `accountingConnectorBexio.py` |
+| 1.1.10 | Integration Abacus | ✅ | `accountingConnectorAbacus.py` |
+| 1.1.11 | Integration Xero | ❌ | Kein `accountingConnectorXero.py` — kein Code vorhanden |
+| 1.1.12 | PDF/Excel/Word/Zip Verarbeitung | ✅ | Extraktoren vorhanden; UDM-Konzept in `0-ideas/unified-document-model.md` |
+
+### 1.2 Budget / Soll-Ist-Vergleich
+
+**Kunden:** Bling (Prio hoch), Quid (KPI-Kontext)
+
+| # | Anforderung | Codebase-Status | Evidenz / Bemerkung |
+|---|-------------|-----------------|---------------------|
+| 1.2.1 | Budget-Excel hochladen und gegen Live-Daten prüfen | 🔧 | Prompt-Template "Budget-Vergleich" in `mainTrustee.py` (Quick Action + Template Workflow); Frontend `TrusteeAnalyseView.tsx` hat Budget-Tab mit Upload; **aber kein Demo-Budget-Excel vorhanden** |
+| 1.2.2 | Vergleiche über Perioden (Q1 aktuell vs. Vorjahr vs. Budget) | ✅ | Im Prompt-Template als Anweisung an AI-Agent enthalten |
+| 1.2.3 | Automatische Diagramm-Erstellung | ✅ | Agent erzeugt Charts via `aggregateTable` + AI-Prompt |
+| 1.2.4 | Live-Daten via API (kein PDF-Export nötig) | ✅ | `refreshAccountingData` Action synct Live-Daten; API-basiert |
+| 1.2.5 | Caching konfigurierbar | ✅ | `_featureQueryCache` mit TTL 300s in `_featureSubAgentTools.py` |
+| 1.2.6 | Abweichungen mit Begründung | ✅ | Im Prompt-Template: Agent soll Abweichungen erklären |
+| 1.2.7 | Vorausschauende Prognosen | ✅ | Separates Prompt-Template "Prognose/Trend-Analyse" |
+
+### 1.3 Cashflow-Rechnung
+
+**Kunden:** Bling
+
+| # | Anforderung | Codebase-Status | Evidenz / Bemerkung |
+|---|-------------|-----------------|---------------------|
+| 1.3.1 | Plausibilisierung und Erstellung Cashflow-Rechnung | ✅ | Prompt-Template "Cashflow-Rechnung" in `mainTrustee.py` |
+| 1.3.2 | Nicht-relevante Positionen berücksichtigen | ✅ | Im Prompt-Template als Anweisung |
+| 1.3.3 | Warnungen bei kritischen Werten | ✅ | Im Prompt-Template |
+
+### 1.4 Dashboard — Bilanz- & Erfolgsrechnungsanalyse
+
+**Kunden:** Bling, Quid
+
+| # | Anforderung | Codebase-Status | Evidenz / Bemerkung |
+|---|-------------|-----------------|---------------------|
+| 1.4.1 | KPI-Dashboard (Bruttogewinn, ROI, Gewinn etc.) | ✅ | Prompt-Template "KPI-Dashboard" + Quick Action + `TrusteeAnalyseView.tsx` KPI-Tab |
+| 1.4.2 | Check hälftiger Kapitalverlust | ✅ | Im Jahresabschluss-Prompt enthalten |
+| 1.4.3 | Überschuldungs-Check | ✅ | Im Jahresabschluss-Prompt enthalten |
+| 1.4.4 | Durchschnittliche Zahlungsfrist | 🔧 | Nicht als dedizierter KPI; Agent kann es berechnen wenn Daten vorhanden |
+
+### 1.5 Liquiditätsplanung
+
+**Kunden:** Bling
+
+| # | Anforderung | Codebase-Status | Evidenz / Bemerkung |
+|---|-------------|-----------------|---------------------|
+| 1.5.1 | Liquiditätsplanung analog Budgetplanung | 🔧 | Kein dediziertes Prompt-Template; über Prognose-Template teilweise abdeckbar |
+| 1.5.2 | Automatische Erstellung aus Vergangenheit | 🔧 | Agent kann historische Daten analysieren; kein dedizierter Workflow |
+| 1.5.3 | Zusätzliche Inputs (z.B. ausserordentliche Dividende) | ❌ | Kein Mechanismus für manuelle Zusatz-Inputs in Prognose |
+
+### 1.6 Gastro-Use-Case — Echtzeit-Rentabilitätsanalyse
+
+**Kunden:** Bling (Prio hoch, bestes Beispiel für Kunden-Mehrwert)
+
+| # | Anforderung | Codebase-Status | Evidenz / Bemerkung |
+|---|-------------|-----------------|---------------------|
+| 1.6.1 | Integration Kassensystem + Lohndaten + Buchhaltung | ⏸️ | **Wartet auf Kevin-Input** — kein Connector für Kassensysteme/Virux |
+| 1.6.2 | Tagesumsatz vs. Personalkosten vs. Wareneinsatz | ⏸️ | Konzeptionell über generischen Data-Import möglich |
+
+### 1.7 Abschlussunterstützung
+
+**Kunden:** Bling, allgemein Treuhand
+
+| # | Anforderung | Codebase-Status | Evidenz / Bemerkung |
+|---|-------------|-----------------|---------------------|
+| 1.7.1 | Jahresabschluss-Checks | ✅ | Prompt-Template "Jahresabschluss prüfen" in `mainTrustee.py` |
+| 1.7.2 | Abgrenzungsbuchungen vorbereiten | 🔧 | Im Prompt adressiert; keine dedizierte Automation |
+| 1.7.3 | Bilanzkonti prüfen / Saldovalidierung | ✅ | Im Jahresabschluss-Prompt |
+| 1.7.4 | Vorjahresvergleiche | ✅ | In mehreren Prompt-Templates |
+
+### 1.8 Zentrales Mandantenmanagement
+
+**Kunden:** Bling (verschiedene Systeme über eine Schaltzentrale)
+
+| # | Anforderung | Codebase-Status | Evidenz / Bemerkung |
+|---|-------------|-----------------|---------------------|
+| 1.8.1 | Verschiedene Kunden auf verschiedenen Systemen verwalten | ✅ | Multi-Tenant-Plattform ist Kernarchitektur; `accountingRegistry.py` unterstützt verschiedene Connectors pro Mandant |
+| 1.8.2 | Ein Login, alle Mandanten | ✅ | Plattform-Feature: Mandantenwechsel im UI |
+| 1.8.3 | Strikte Datentrennung | ✅ | Mandantenisolation auf DB- und API-Ebene |
+
+---
+
+### 1.9 PWG — Stiftung für preisgünstiges Wohnen
+
+**Kontext:** ~300 Liegenschaften, Team 7–8 Personen. IT-Leiter Markus Brütsch (seit 2+ Jahren). Neue Rolle "Organella" für Digitalisierung (Schnittstelle Technologie/Betrieb, kein IT-Hintergrund). PWG evaluiert AI-Lösungen offen, noch keine Entscheidung getroffen. Workshop 16.04.2026 durchgeführt.
+
+**Pilotprojekt bestätigt:** Jahresmietzinsbestätigungen (siehe 1.9.9).
+
+#### 1.9a Plattform-Features (bestehend)
+
+| # | Anforderung | Codebase-Status | Evidenz / Bemerkung |
+|---|-------------|-----------------|---------------------|
+| 1.9.1 | Automatisierte Belegverarbeitung mit Abacus | ✅ | `accountingConnectorAbacus.py` + Trustee-Pipeline |
+| 1.9.2 | Kommunikations-Coach für Mietergespräche | ✅ | `features/commcoach/` — komplett inkl. 10+ Personas |
+| 1.9.3 | Immobilien-Personas (Zahlungsrückstand, Nebenkosten, Einzug, Lärm) | ✅ | 4 PWG-Personas in `BUILTIN_PERSONAS` + Seeding |
+| 1.9.4 | KI-Arbeitsplatz mit Datenneutralisierung | ✅ | `features/neutralization/` — PII-Masking, Playground, Private-LLM; Daten-Residency Schweiz |
+| 1.9.5 | PWG-Agent (Stiftungsstil, öffentliche PDFs als Futter) | 🔧 | Knowledge-Base-Feature vorhanden; kein PWG-spezifisches Knowledge-Set (Geschäftsberichte, Vermietungsreglement, Führungshandbuch etc.) |
+| 1.9.6 | M365-Anbindung (SharePoint, Outlook, OneDrive, Teams) | ✅ | SharePoint-Nodes + Outlook-Methode in Workflows; nutzt nativen SharePoint-Index |
+| 1.9.7 | KI-Auswertung Abacus-Daten/Reports | ✅ | Über Trustee-Feature + `aggregateTable` + AI-Prompts; Read/Write via Abacus-API |
+| 1.9.8 | Grundstücksanalyse (öffentliche Daten: GIS, Maps, Grundbuch) | ❌ | Kein Connector/Workflow für öffentliche Geodaten |
+
+#### 1.9b Neue Anforderungen aus Workshop 16.04.2026
+
+| # | Anforderung | Codebase-Status | Evidenz / Bemerkung |
+|---|-------------|-----------------|---------------------|
+| 1.9.9 | **Pilotprojekt: Jahresmietzinsbestätigungen** (ca. 700–800 Schreiben/Quartal, Versand Sommer 2026) | ❌ | Kein dedizierter Workflow — siehe Workflow-Design unten |
+| 1.9.10 | FileMaker-Integration (Portfolio, Liegenschaften, Erneuerungsplanung, Bauprojekte, Akquisition) | ❌ | Kein FileMaker-Connector; Datenmenge/-qualität muss geprüft werden |
+| 1.9.11 | Template-basierte Dokumentenerstellung (HTML-Templates → Word mit Corporate Design) | 🔧 | Code-Editor mit Claude vorhanden; kein Template-to-Word-Pipeline |
+| 1.9.12 | Notification-System (reagiert auf DB-Änderungen, Berichte per E-Mail, Workflow-Trigger) | 🔧 | E-Mail-Versand in Workflows vorhanden (`methodOutlook`); kein generisches DB-Change-Detection-System |
+| 1.9.13 | Information Retrieval über diverse Wissensquellen (Guidelines, Handbücher, SharePoint) | 🔧 | Knowledge-Base + SharePoint-Index vorhanden; kein PWG-spezifisches Datenset geladen |
+| 1.9.14 | Datenvalidierung als Erst-Schritt (welche Daten korrekt/aktuell?) | ❌ | Kein Daten-Audit-Workflow |
+| 1.9.15 | Compliance & Audit: vollständiges Tracking aller AI-Transaktionen | ✅ | `ComplianceAuditPage.tsx` + Gateway-Logging; jede Anfrage protokolliert |
+| 1.9.16 | Granulare Neutralisierungs-Kontrolle pro Datenquelle | ✅ | In Neutralisierungs-Feature konfigurierbar pro Source |
+
+#### 1.9c Pilot-Workflow: Jahresmietzinsbestätigungen
+
+**Business Case:** 4 × 800 Schreiben/Jahr = 3'200 Schreiben. Aktuell: Serienbrief → manueller Versand → Scan der Rückantworten → manuelle Verarbeitung. Ziel: AI-gestützte Verarbeitung der gescannten Rückantworten mit Antwortvorschlägen.
+
+**Workflow-Design (6–7 Schritte, davon 1 mit AI):**
+
+```
+┌─────────────────────────────────────────────────────────────────┐
+│ PWG Pilot: Jahresmietzinsbestätigungen │
+│ │
+│ Phase 1: Generierung (bestehend bei PWG) │
+│ ┌──────────────────────────────────────┐ │
+│ │ System generiert Serienbrief │ (PWG-internes System) │
+│ │ mit aktuellen Mietdaten │ │
+│ └──────────┬───────────────────────────┘ │
+│ ▼ │
+│ Phase 2: Versand & Rücklauf (manuell) │
+│ ┌──────────────────────────────────────┐ │
+│ │ Manueller Versand + Scan der │ │
+│ │ Rückantworten → SharePoint-Ordner │ │
+│ └──────────┬───────────────────────────┘ │
+│ ▼ │
+│ Phase 3: PowerOn-Workflow (automatisiert) │
+│ │
+│ Step 1: trigger.schedule (täglich oder on-demand) │
+│ ▼ │
+│ Step 2: sharepoint.listFiles (Scan-Ordner, neue Dokumente) │
+│ ▼ │
+│ Step 3: flow.loop (für jedes gescannte Dokument) │
+│ ▼ │
+│ Step 4: sharepoint.downloadFile + trustee.extractFromFiles │
+│ → OCR/Extraktion: Mietername, Adresse, Bestätigung, │
+│ Anmerkungen, Unterschrift ja/nein │
+│ ▼ │
+│ Step 5: ai.prompt ← EINZIGER AI-SCHRITT │
+│ → Gescannte Daten gegen Originaldaten prüfen │
+│ → Status klassifizieren (bestätigt / Abweichung / │
+│ fehlende Unterschrift / unleserlich) │
+│ → Antwortvorschlag generieren bei Abweichung │
+│ ▼ │
+│ Step 6: data.writeToTable (Ergebnis in Übersichtstabelle) │
+│ ▼ │
+│ Step 7: email.send (Zusammenfassung an Sachbearbeiter) │
+│ → Audit-Log für jeden Verarbeitungsschritt │
+└─────────────────────────────────────────────────────────────────┘
+```
+
+**Benötigte Komponenten für den Pilot-Workflow:**
+
+| # | Komponente | Codebase-Status | Was zu tun ist |
+|---|-----------|-----------------|----------------|
+| W1 | `trigger.schedule` oder `trigger.manual` | ✅ | Vorhanden in `nodeDefinitions/triggers.py` |
+| W2 | `sharepoint.listFiles` + `sharepoint.downloadFile` | ✅ | Vorhanden in `nodeDefinitions/sharepoint.py` |
+| W3 | `flow.loop` | ✅ | Vorhanden in `nodeDefinitions/flow.py` |
+| W4 | `trustee.extractFromFiles` (OCR/Extraktion gescannter Dokumente) | ✅ | Vorhanden; OCR für einseitige Scans unterstützt |
+| W5 | `ai.prompt` — Prompt-Template "Mietzinsbestätigung prüfen" | ❌ | **Neues Prompt-Template** nötig: Scan-Daten gegen Originaldaten abgleichen, Status klassifizieren, Antwortvorschlag generieren |
+| W6 | Ergebnis-Tabelle / Übersichtsliste (verarbeitete Bestätigungen) | 🔧 | `data`-Nodes existieren; kein dedizierter "Mietzinsbestätigungs-Report"-Output |
+| W7 | `email.send` (Zusammenfassung an Sachbearbeiter) | ✅ | Vorhanden via `methodOutlook` |
+| W8 | Abacus-Referenzdaten (Original-Mietzinsdaten für Abgleich) | 🔧 | Abacus-Connector vorhanden; Abfrage der Mietzins-Stammdaten muss konfiguriert werden |
+| W9 | Audit-Logging für gesamten Prozess | ✅ | Plattform-Feature: alle AI-Transaktionen geloggt |
+| W10 | Graph-Editor Workflow als Template speichern | ❌ | Workflow muss im Editor gebaut und als System-Template gespeichert werden |
+
+**Voraussetzungen (Action Items aus Workshop):**
+
+| # | Action Item | Verantwortlich | Status |
+|---|------------|----------------|--------|
+| AI1 | API-Zugang zu Abacus-Testmandant einrichten | Patrick Motsch | ❌ offen — Patrick koordiniert mit Abacus |
+| AI2 | PWG muss Zugriff auf Testmandant bestätigen | PWG (Markus) | ❌ offen |
+| AI3 | Preismodell bereitstellen (Grundgebühr/User/Monat + Token-Gebühr) | PowerOn | ❌ offen |
+| AI4 | Datenschutzanforderungen und Vertragsklauseln für Lieferanten prüfen | PWG | ❌ offen |
+| AI5 | Prozessschritte Pilot ausarbeiten und Kalkulation erstellen | PowerOn | 🔧 Workflow-Design in diesem Dokument; Kalkulation offen |
+| AI6 | FileMaker-Datenmenge und -qualität prüfen (potenzielle Integration) | PWG + PowerOn | ❌ offen |
+
+---
+
+### 1.10 Quid / ServiceHunter Use Cases
+
+| # | Anforderung | Codebase-Status | Evidenz / Bemerkung |
+|---|-------------|-----------------|---------------------|
+| 1.10.1 | KPI-Dashboard (Kundenmargen, ACV, Lifetime Value, Produktionsmarge) | 🔧 | Generisches KPI-Prompt-Template vorhanden; kundenspezifische KPIs brauchen Daten-Import |
+| 1.10.2 | Zeiterfassung & Support-Analyse (Zendesk-Verknüpfung) | ⏸️ | **Kein Zendesk-Connector** — bewusst CSV-Upload-Workaround |
+| 1.10.3 | Prognosen & Proaktive Steuerung | ✅ | Prognose-Prompt-Template vorhanden |
+| 1.10.4 | Konsolidierung international (CH, DE, UK) | ⏸️ | **Wartet auf Lars-Meeting** — hohe Komplexität |
+| 1.10.5 | Spesen-Automatisierung (SharePoint/Drive → verbuchen) | ✅ | SharePoint-Pipeline + Trustee-Import vorhanden |
+
+---
+
+## Teil 2: UI/UX-Anforderungen (aus Team-Meeting / Nutzertests)
+
+### 2.1 AI-Workspace Usability
+
+| # | Anforderung | Codebase-Status | Evidenz / Bemerkung |
+|---|-------------|-----------------|---------------------|
+| 2.1.1 | Primäre Prompt-Zeile dominant sichtbar (kein "Wo tippe ich?") | 🔧 | `WorkspaceInput.tsx` funktional komplett; **Placeholder auf Englisch**, keine Hero-Eingabe, sekundäre Aktionen in einer Zeile |
+| 2.1.2 | Empty State im Chat (Orientierung für Erstnutzer) | ❌ | `ChatStream.tsx` zeigt blank area wenn `messages.length === 0` — **kein Welcome/Empty-State** |
+| 2.1.3 | "Neuer Chat" sichtbar ohne UDB-Sidebar | ❌ | Nur `+` in `ChatsTab` Toolbar; kein zentraler CTA |
+| 2.1.4 | Datei-Drop Entdeckbarkeit | 🔧 | Vollflächen-Drop existiert in `WorkspacePage.tsx`; kein dauerhafter Hinweis im leeren Chat |
+| 2.1.5 | DE-Placeholder und i18n | ❌ | Placeholder noch auf Englisch (`Type a message...`) |
+
+### 2.2 Responsive Layout & Breakpoints
+
+| # | Anforderung | Codebase-Status | Evidenz / Bemerkung |
+|---|-------------|-----------------|---------------------|
+| 2.2.1 | Zwischen-Breakpoint (1025–1280px): Sidebars auto-einklappen | ❌ | `isMobile` nur bei `≤1024px`; schmale Desktop-Fenster → Mittelspalte zu schmal |
+| 2.2.2 | Visuelle Hierarchie Prompt (Schatten/Rand, minHeight) | ❌ | `WorkspaceInput.tsx` ohne besondere Hervorhebung |
+| 2.2.3 | Viewport-Testmatrix (1100/1200/1280/1440) | ❌ | Kein Test-Setup dafür |
+
+### 2.3 Vertrauen & Marketing-UI
+
+| # | Anforderung | Codebase-Status | Evidenz / Bemerkung |
+|---|-------------|-----------------|---------------------|
+| 2.3.1 | Trust-Badges (Daten hosted in der Schweiz, Anbieter/Standort) | ❌ | Login: **kein** Hosting-Hinweis; keine `TrustFooter`/`TrustStrip` Komponente |
+| 2.3.2 | "Recommended by" Partner-Strip (Valion, PamoCreate, Swiss AI Association etc.) | ❌ | Keine Partner-Logos/Links |
+| 2.3.3 | Swiss ® am Logo | ❌ | Nur im Bildasset, nicht in UI sichtbar (Legal klären) |
+| 2.3.4 | Sicherheits-Banner in SourcesTab ("Verbindung ist read-only") | ❌ | `SourcesTab.tsx` ohne Info-Banner |
+
+### 2.4 Billing & Pricing Auffindbarkeit
+
+| # | Anforderung | Codebase-Status | Evidenz / Bemerkung |
+|---|-------------|-----------------|---------------------|
+| 2.4.1 | Billing-Funktionalität (komplett) | ✅ | `BillingDataView.tsx` mit Tabs, Balance Cards, Transaktionen |
+| 2.4.2 | Einstieg Billing nach Login (Teaser/Banner) | ❌ | Kein Dashboard-Teaser; Zugang nur via Nav-Baum + User-Menü |
+| 2.4.3 | Balance im UserSection (neben Avatar) | ❌ | Nur Menü-Eintrag "Guthaben" |
+| 2.4.4 | Billing-Copy für Nicht-Admins | ❌ | Kein Hinweis "Frag deinen Admin" |
+
+### 2.5 Onboarding & Erstnutzung
+
+| # | Anforderung | Codebase-Status | Evidenz / Bemerkung |
+|---|-------------|-----------------|---------------------|
+| 2.5.1 | Connector-Onboarding ("sicher verbinden, nichts kaputt") | ❌ | Keine Microcopy in SourcesTab |
+| 2.5.2 | Progressive Offenlegung (weniger gleichzeitig auf Startscreens) | ❌ | Workspace zeigt alles parallel |
+
+---
+
+## Teil 3: Infrastruktur & Demo-Vorbereitung
+
+### 3.1 Demo-Konfigurationen
+
+| # | Item | Codebase-Status | Evidenz / Bemerkung |
+|---|------|-----------------|---------------------|
+| 3.1.1 | Demo-Config-Infrastruktur (Base + Admin API) | ✅ | `_baseDemoConfig.py`, `routeAdminDemoConfig.py` |
+| 3.1.2 | Referenz: `investorDemo2026.py` | ✅ | Vollständiges Muster mit Mandant, User, Features, Billing |
+| 3.1.3 | Demo-Mandant "Bling Demo" | ❌ | Kein `blingDemo2026.py` |
+| 3.1.4 | Demo-Mandant "PWG Demo" | ❌ | Kein `pwgDemo2026.py` |
+| 3.1.5 | Demo-Mandant "Quid Demo" | ❌ | Kein `quidDemo2026.py` |
+
+### 3.2 Testdaten
+
+| # | Item | Codebase-Status | Evidenz / Bemerkung |
+|---|------|-----------------|---------------------|
+| 3.2.1 | Fiktives Mieterdossier (Neutralisierung) | ✅ | `demoData/neutralizer/tenant-dossier.pdf` + Generator |
+| 3.2.2 | Knowledge-Base Demo-Dateien | ✅ | `demoData/knowledge-base/` — 4 Dateien |
+| 3.2.3 | Budget-Excel (Soll-Werte) | ❌ | Kein `.xlsx` im Repo |
+| 3.2.4 | Musterbelege (Rechnung, Spesen, Bank, Versicherung) | ❌ | Keine Demo-Belege |
+| 3.2.5 | Quid-Testdaten (CSV: Umsatz, Kunden, Support) | ❌ | Keine kundenspezifischen Testdaten |
+| 3.2.6 | Bling-Testdaten (Bexio-kompatibel) | ❌ | Keine Bexio-Testdaten |
+| 3.2.7 | PWG-Testdaten (Abacus-kompatibel) | ❌ | Keine Abacus-Testdaten |
+
+### 3.3 Demo-Workflows & Skripte
+
+| # | Item | Codebase-Status | Evidenz / Bemerkung |
+|---|------|-----------------|---------------------|
+| 3.3.1 | System-Template "Treuhand: PDF-Klassifizierung" | ✅ | In `interfaceBootstrap.py` |
+| 3.3.2 | Demo-Workflow (manual trigger → SharePoint → Trustee Pipeline) | ❌ | System-Template existiert, aber kein dedizierter Demo-Workflow |
+| 3.3.3 | Demo-Skript Bling | ❌ | |
+| 3.3.4 | Demo-Skript PWG | ❌ | |
+| 3.3.5 | Demo-Skript Quid | ❌ | |
+| 3.3.6 | Neutralisierungs-Demo-Flow (Schritt-für-Schritt) | ❌ | Kein Skript |
+
+---
+
+## Teil 4: Allgemeine Treuhand-Use-Cases (Prozessdokumentation)
+
+Diese Use Cases stammen aus der allgemeinen Treuhand-Prozessdokumentation und sind für **alle** Kunden relevant.
+
+### 4.1 Datenerfassung — Kleine Unternehmen
+
+| # | Anforderung | Codebase-Status |
+|---|-------------|-----------------|
+| 4.1.1 | Belege sortieren & mit Banktransaktionen referenzieren | 🔧 — Belegverarbeitung vorhanden; kein Bank-Statement-Matching |
+| 4.1.2 | Import Banktransaktionen via Excel/CSV | 🔧 — File-Upload + Extraktion vorhanden; kein dedizierter Bank-Import |
+| 4.1.3 | Automatisierte Rückfragen an Kunden bei fehlenden Belegen | ❌ |
+
+### 4.2 Lohnbuchhaltung
+
+| # | Anforderung | Codebase-Status |
+|---|-------------|-----------------|
+| 4.2.1 | Überleitung Lohnaufwand (Fibu vs. Lohnbuchhaltung) | ❌ — Kein Lohn-Feature |
+| 4.2.2 | Nicht-lohnwirksame Buchungen identifizieren | ❌ |
+| 4.2.3 | Fehlende Lohnmeldungen erkennen | ❌ |
+
+### 4.3 Steuererklärung
+
+| # | Anforderung | Codebase-Status |
+|---|-------------|-----------------|
+| 4.3.1 | Belege nach Kategorien ordnen | 🔧 — Klassifikation vorhanden |
+| 4.3.2 | Steuererklärung erstellen | ❌ — Kein Steuer-Feature |
+| 4.3.3 | Vorjahresvergleich und Plausibilisierung | ✅ — In Analyse-Prompts |
+
+### 4.4 Jahresabschluss (Mittlere/Grosse Unternehmen)
+
+| # | Anforderung | Codebase-Status |
+|---|-------------|-----------------|
+| 4.4.1 | Banksalden abgleichen (Buchhaltung vs. Bank) | 🔧 — Konzeptionell via Agent; kein dedizierter Check |
+| 4.4.2 | PayPal, Revolut, ausländische Konten nachbuchen | ❌ |
+| 4.4.3 | Wertschriftendepot nachbuchen | ❌ |
+| 4.4.4 | Fehlerkonto-Transaktionen bereinigen | ❌ |
+| 4.4.5 | Kreditkartenkonto abstimmen | ❌ |
+| 4.4.6 | Zahlungsanbieter prüfen (Stripe, Amex) | ❌ |
+| 4.4.7 | Nebenbücher mit Fibu abstimmen | ❌ |
+| 4.4.8 | Skonto-Differenzen ausbuchen | ❌ |
+| 4.4.9 | Konzerninterne Abstimmung | ❌ |
+| 4.4.10 | Darlehenskonten abgleichen, Zinsen berechnen | ❌ |
+| 4.4.11 | Fremdwährungsbewertung gemäss ESTV | ❌ |
+| 4.4.12 | Eigenkapitalveränderungen mit HR abgleichen | ❌ |
+| 4.4.13 | Umsatz-/Aufwandsplausibilisierung | ✅ — Im Jahresabschluss-Prompt |
+| 4.4.14 | Rechnungsabgrenzungen (aktiv/passiv) | 🔧 — Im Prompt adressiert |
+| 4.4.15 | Abschreibungen erfassen | ❌ |
+| 4.4.16 | MWST-Jahresabstimmung | ❌ |
+| 4.4.17 | Steueraufwand-Rückstellungen berechnen | ❌ |
+| 4.4.18 | Bilanz und ER plausibilisieren | ✅ — Im Jahresabschluss-Prompt |
+| 4.4.19 | Analyse gesetzliche Bestimmungen (Kapitalverlust, Überschuldung) | ✅ — Im Jahresabschluss-Prompt |
+
+### 4.5 Finanzielle Führung & Controlling
+
+| # | Anforderung | Codebase-Status |
+|---|-------------|-----------------|
+| 4.5.1 | Businessplan → Absatzplanung → Ertragsplanung | ❌ |
+| 4.5.2 | Investitionsplan und Finanzplan | ❌ |
+| 4.5.3 | Budget mit Szenarien | 🔧 — Budget-Prompt vorhanden; Szenarien via Chat |
+| 4.5.4 | Soll/Ist-Vergleich mit Abweichungsanalyse | ✅ — Budget-Vergleich Prompt-Template |
+| 4.5.5 | Deckungsbeitragsrechnung | ❌ |
+| 4.5.6 | Projekt-Controlling | ❌ |
+| 4.5.7 | Benchmarkanalyse | ❌ |
+| 4.5.8 | KPI-Monitoring mit proaktiver Benachrichtigung | 🔧 — KPIs via Agent; kein proaktives Alert-System |
+
+### 4.6 Revisionen
+
+| # | Anforderung | Codebase-Status |
+|---|-------------|-----------------|
+| 4.6.1 | Ordentliche Revisionen unterstützen | ❌ — Kein Revisions-Feature |
+
+### 4.7 Steueroptimierung & Simulationen
+
+| # | Anforderung | Codebase-Status |
+|---|-------------|-----------------|
+| 4.7.1 | Jahresrechnung auf steuerliche Probleme prüfen | ❌ |
+| 4.7.2 | Simulationen (Lohn vs. Dividende, Umzug, 3. Säule, BVG) | ❌ |
+| 4.7.3 | MWST-Prüfung, VST-Korrekturen | ❌ |
+| 4.7.4 | Steuerausscheidungen auf verschiedene Kantone | ❌ |
+
+### 4.8 Interdisziplinäre Themen
+
+| # | Anforderung | Codebase-Status |
+|---|-------------|-----------------|
+| 4.8.1 | Umstrukturierungen | ❌ |
+| 4.8.2 | Steuer-/Vorsorgestrategien | ❌ |
+| 4.8.3 | Nachfolgeplanung | ❌ |
+| 4.8.4 | Unternehmensbewertungen | ❌ |
+
+---
+
+## Zusammenfassung: Status-Überblick
+
+### Fertig (✅) — Kernplattform funktioniert
+
+| Bereich | Was steht |
+|---------|-----------|
+| **Trustee Agent-Tools** | `refreshTrusteeData`, `aggregateTable`, Connection-Pooling, Result-Caching |
+| **Graph-Editor** | Trustee-Kategorie + 4 Nodes, SharePoint-Nodes, Flow-Nodes (Loop, If/Else, Switch, Merge) |
+| **Prompt-Templates** | 5 Analyse-Typen (Budget, KPI, Cashflow, Prognose, Jahresabschluss) als Quick Actions + Template Workflows |
+| **Accounting-Connectors** | Bexio, Abacus, RunMyAccounts |
+| **Belegverarbeitung** | Extraktion → Klassifikation → Kontierung → Sync (end-to-end) |
+| **CommCoach** | Feature komplett, 10+ Personas inkl. 4 Immobilien-Personas (PWG), Gamification, Seeding |
+| **Neutralisierung** | PII-Masking, Playground, Private-LLM, Mieterdossier-PDF |
+| **Multi-Tenancy** | Mandantenisolation, Datentrennung, Rollenwechsel |
+| **Billing** | BillingDataView, Balance, Transaktionen, useBilling |
+| **SharePoint-Integration** | 6 Workflow-Nodes + Automation |
+| **Demo-Infrastruktur** | Base-Config, Admin-API, Investor-Demo als Referenz |
+
+### Teilweise (🔧) — Grundstruktur steht, Erweiterung nötig
+
+| Bereich | Was fehlt |
+|---------|-----------|
+| Firmen-Mapping Kunden/Lieferanten | Regelbasiertes Matching-UI |
+| Beleg-Tagging | Beleg-spezifisches Tag-System |
+| Vorsteuer-Automatisierung | MWST-Zuordnung bei Belegverarbeitung |
+| Liquiditätsplanung | Dediziertes Prompt-Template |
+| Workspace-Prompt | DE-Placeholder, visuelle Hervorhebung |
+| Datei-Drop | Hinweis im Empty State |
+| Budget-Prompt | Demo-Excel-Datei fehlt |
+
+### Offen (❌) — Noch zu bauen
+
+#### Prio 0: PWG-Pilot (Versand Sommer 2026 — Deadline-gebunden)
+
+| Item | Aufwand | Beschreibung |
+|------|---------|-------------|
+| Abacus-Testmandant API-Zugang | Extern | Patrick koordiniert mit Abacus; PWG muss Zugriff bestätigen |
+| Prompt-Template "Mietzinsbestätigung prüfen" | Mittel | Neues Template: Scan vs. Originaldaten, Status-Klassifikation, Antwortvorschlag |
+| Pilot-Workflow im Graph-Editor | Mittel | trigger → sharepoint.listFiles → loop → download → extract → ai.prompt → report → email |
+| Abacus-Mietzins-Stammdaten-Abfrage | Klein | Konfiguration im Abacus-Connector für Mietzins-Referenzdaten |
+| Demo-Mandant PWG | Mittel | `pwgDemo2026.py` — Trustee (Abacus), CommCoach, Neutralisierung, Workspace |
+| PWG Knowledge-Set | Klein | Öffentliche PDFs (Geschäftsberichte, Vermietungsreglement) in Knowledge-Base laden |
+| Preismodell / Kalkulation Pilot | Extern | Grundgebühr/User/Monat + Token-Gebühr; basierend auf 4×800 Schreiben/Jahr |
+
+#### Prio 1: Demo-Blocker / Weitere Kunden-Demos
+
+| Item | Aufwand | Beschreibung |
+|------|---------|-------------|
+| Demo-Mandant Bling | Mittel | `blingDemo2026.py` — Trustee (Bexio), Workspace, Graph-Editor |
+| Demo-Mandant Quid | Klein | `quidDemo2026.py` — Workspace, Graph-Editor, CSV-Upload |
+| Budget-Excel | Klein | Soll-Werte 2026 für Demo |
+| Musterbelege (PDFs) | Klein | 3–5 Belege (Rechnung, Spesen, Bank, Versicherung) |
+| Demo-Skripte | Klein | Schritt-für-Schritt pro Kunde |
+| Neutralisierungs-Demo-Flow | Klein | Dokumentation |
+| Demo-Workflow Graph-Editor (generisch) | Mittel | trigger.manual → SharePoint → Trustee Pipeline |
+
+#### Prio 2: UI/UX-Verbesserungen (Erstnutzer-Hürde senken)
+
+| Item | Aufwand | Beschreibung |
+|------|---------|-------------|
+| Empty State ChatStream | Klein | Titel, Bullets, "Neuer Chat" Button |
+| DE-Placeholder + i18n | Klein | `WorkspaceInput.tsx` |
+| "Neuer Chat" CTA in Mitte | Klein | `WorkspacePage.tsx` |
+| Zwischen-Breakpoint (1025–1280px) | Mittel | Sidebar-Auto-Collapse |
+| Trust-Strip/Footer (Login, Landing) | Mittel | Hosting Schweiz, Partner-Logos (Legal nötig) |
+| Billing-Teaser nach Login | Klein | Link oder Banner |
+| Connector-Onboarding Copy | Klein | Sicherheits-Banner in SourcesTab |
+| Balance im UserSection | Klein | Zahl neben Avatar |
+
+#### Prio 3: Feature-Erweiterungen (Roadmap)
+
+| Item | Aufwand | Beschreibung |
+|------|---------|-------------|
+| Xero-Connector | Gross | Neuer Accounting-Connector |
+| FileMaker-Connector (PWG) | Gross | Portfolio, Liegenschaften, Erneuerungsplanung; Datenmenge/-qualität erst prüfen |
+| Buchungsregeln für wiederkehrende Belege | Mittel | Regel-Engine |
+| Liquiditätsplanungs-Template | Klein | Neues Prompt-Template |
+| KPI-Monitoring mit Alerts | Gross | Proaktives Benachrichtigungssystem |
+| Template-basierte Dokumentenerstellung (HTML → Word) | Mittel | Erneuerungsstrategien, Corporate Design |
+| DB-Change-Detection / Notification-Trigger | Mittel | Reagiert auf Datenbank-Änderungen, triggert Workflows |
+| Datenvalidierungs-Workflow | Klein | Erst-Audit: welche Daten korrekt/aktuell |
+
+#### Prio 4: Zurückgestellt (wartet auf externen Input)
+
+| Item | Wartet auf | Kunde |
+|------|-----------|-------|
+| Gastro-Echtzeit-Integration | Kevin (Bling) soll UC ausformulieren | Bling |
+| Zendesk-Connector | Bewusst CSV-Workaround | Quid |
+| Regelbasierte Konsolidierung | Lars-Meeting | Quid |
+| Grundstücksanalyse (GIS etc.) | Kundenpräzisierung | PWG |
+| FileMaker-Integration (PWG) | Datenmenge/-qualität prüfen (PWG + PowerOn) | PWG |
+| PWG Datenschutz-/Vertragsklauseln | PWG prüft intern (genossenschaftliche Struktur) | PWG |
+
+#### Langfristig: Allgemeine Treuhand-Automatisierung
+
+Die Use Cases aus Teil 4 (Lohnbuchhaltung, Steuererklärung, detaillierter Jahresabschluss, Revisionen, Steueroptimierung, interdisziplinäre Themen) sind **konzeptionell dokumentiert** aber noch **nicht in der Codebase**. Diese bilden die Langfrist-Roadmap und werden schrittweise über AI-Prompt-Templates und dedizierte Workflows umgesetzt, sobald die Kern-Use-Cases bei den ersten Kunden validiert sind.
+
+---
+
+## Entscheidungen
+
+| Datum | Entscheidung | Begründung |
+|-------|-------------|------------|
+| 2026-04-07 | Prompt-Templates als Code in `mainTrustee.py` | Wartbar und versioniert statt externe Dateien |
+| 2026-04-07 | `refreshTrusteeData` als separate Action | Separation of Concerns: Sync = schreibend, Query = lesend |
+| 2026-04-07 | DB-Connection-Pooling statt Connection-per-Call | Grösster Performance-Hebel (~200ms pro Connection) |
+| 2026-04-09 | Quid ohne Zendesk-Connector — CSV-Upload | Gleicher Analyse-Mehrwert, viel weniger Aufwand |
+| 2026-04-09 | Gastro-UC und Konsolidierung auf Prio 4 | Warten auf Kunden-Input |
+| 2026-04-09 | CommCoach-Personas als schnellster Wow-Effekt für PWG | Feature gebaut, nur Persona-Definitionen nötig |
+| 2026-04-07 | UI-Enhancements: Fokus Erstnutzer-Hürde | Ohne klare Userführung springen Kunden ab |
+| 2026-04-07 | Trust-Badges: kein ISO, sondern "Daten in CH" | Konkreter Nutzen > abstraktes Zertifikat |
+| 2026-04-16 | PWG-Pilot = Jahresmietzinsbestätigungen als erster Produktiv-UC | Konkreter, messbarer Business Case (4×800 Schreiben); Deadline Sommer 2026 |
+| 2026-04-16 | Workflow-Philosophie PWG: erst manuell testen, dann automatisieren | Agent wird aus manuell getesteten Prozessen erstellt; Workflows für wiederkehrende Aufgaben |
+| 2026-04-16 | FileMaker-Integration auf Prio 4 | Datenmenge/-qualität muss erst geprüft werden; kein Blocker für Pilot |
+| 2026-04-16 | Abacus-Testmandant als Voraussetzung für Pilot | Patrick koordiniert API-Zugang direkt mit Abacus |
+
+---
+
+## Betroffene Module
+
+- **Gateway:** `features/trustee/` (Prompts, Quick Actions, Connectors), `features/commcoach/` (Personas), `features/neutralization/` (Demo-Config), `features/graphicalEditor/` (Nodes), `serviceCenter/services/serviceAgent/` (Tools, Caching), `demoConfigs/` (neue Configs), `workflows/methods/methodTrustee/` (Actions), `demoData/` (Testdaten)
+- **Frontend Nyla:** `WorkspacePage.tsx` (Layout, Breakpoints, Empty State), `WorkspaceInput.tsx` (Prompt, i18n), `ChatStream.tsx` (Empty State), `Login.tsx` (Trust-UI), `UserSection.tsx` (Balance), `SourcesTab.tsx` (Onboarding-Copy), `BillingDataView.tsx` (Discovery)
+- **DB-Migration:** Nein
+- **Platform/Wiki:** Nach Release `b-reference/` aktualisieren
+
+## Links
+
+- PWG Workshop-Inputs (16.04.2026): `pamocreate/projects/poweron/customer-pwg/20260415-inputs-pwg.txt`
+- Originale Kunden-Inputs: `local/notes/demo-tue-use-cases-inputs-customers.md`
+- Merged Demo-Plan (ersetzt): `c-work/1-plan/2026-04-demo2-merged-customer-trustee-plan.md`
+- UI-Enhancements Plan (ersetzt): `c-work/1-plan/2026-04-porta-ui-enhancements-team-meeting.md`
+- Investor-Demo (Referenz): `gateway/modules/demoConfigs/investorDemo2026.py`
+- Frontend-Referenz: `b-reference/frontend-nyla/architecture.md`
+- Trustee Main (Prompts): `gateway/modules/features/trustee/mainTrustee.py`
+- CommCoach Personas: `gateway/modules/features/commcoach/serviceCommcoachPersonas.py`
+- Neutralisierung: `gateway/modules/features/neutralization/`
+- Graph-Editor Nodes: `gateway/modules/features/graphicalEditor/nodeDefinitions/`
+- Demo-Daten: `gateway/demoData/`
+- UDM-Konzept: `c-work/0-ideas/unified-document-model.md`
+
+## Abschluss
+
+- [ ] Quell-Dokumente als "superseded by this document" markieren
+- [ ] b-reference/ aktualisiert nach Umsetzung
+- [ ] TOPICS.md aktualisiert
+- [ ] Dieses Dokument → `1-plan/` verschieben wenn Umsetzung startet
diff --git a/c-work/1-plan/2026-04-demo2-merged-customer-trustee-plan.md b/c-work/4-done/2026-04-demo2-merged-customer-trustee-plan.md
similarity index 100%
rename from c-work/1-plan/2026-04-demo2-merged-customer-trustee-plan.md
rename to c-work/4-done/2026-04-demo2-merged-customer-trustee-plan.md
diff --git a/c-work/1-plan/2026-04-porta-ui-enhancements-team-meeting.md b/c-work/4-done/2026-04-porta-ui-enhancements-team-meeting.md
similarity index 100%
rename from c-work/1-plan/2026-04-porta-ui-enhancements-team-meeting.md
rename to c-work/4-done/2026-04-porta-ui-enhancements-team-meeting.md
diff --git a/e-compliance/neutralisierung-detail.md b/e-compliance/neutralisierung-detail.md
index c211e6a..5d5abeb 100644
--- a/e-compliance/neutralisierung-detail.md
+++ b/e-compliance/neutralisierung-detail.md
@@ -1,6 +1,6 @@
# Neutralisierung
-**Stand:** 2026-03-29
+**Stand:** 2026-04-16
---
@@ -18,7 +18,7 @@ Drei Quellen, von breit nach spezifisch:
|--------|-------------|-----------|
| **Feature-Instanz** | `DataNeutraliserConfig.enabled` (hat `featureInstanceId` + `mandateId`) | Alle Daten in dieser Feature-Instanz werden neutralisiert. Betrifft jeden Content-Einstieg innerhalb dieser Instanz. |
| **Chat-Workflow / Session** | `ServiceCenterContext.requireNeutralization` (gesetzt z. B. vom AI-Workspace oder Automation) | Dieser Workflow/Turn: jeder Content, der hier verarbeitet wird, wird neutralisiert. |
-| **Dokument oder Quelle** | `FileItem.neutralize`, `DataSource.neutralize`, `FeatureDataSource.neutralize` | Dieses konkrete Objekt: Content daraus wird neutralisiert, egal ob die Feature-Instanz oder der Workflow es sonst fordern würden. |
+| **Dokument oder Quelle** | `FileItem.neutralize`, `FileFolder.neutralize`, `DataSource.neutralize`, `FeatureDataSource.neutralize`, `FeatureDataSource.neutralizeFields` | Dieses konkrete Objekt: Content daraus wird neutralisiert, egal ob die Feature-Instanz oder der Workflow es sonst fordern würden. `FileFolder.neutralize` propagiert auf alle enthaltenen Dateien. `neutralizeFields` ermoeglicht Feld-Level-Maskierung bei DB-Queries. |
**Auswertung:** Irgendeine Quelle sagt `True` → neutralisieren. Keine Quelle kann eine andere aufheben. Es gibt kein `False`-Override, das ein `True` von woanders aushebelt.
@@ -34,7 +34,9 @@ Am **Punkt der Content-Einspeisung** — dort wo Rohdaten zu verarbeitbarem Cont
| **Content-Extraktion** (Dokumente für KI-Verarbeitung) | Extrahierter Text/Tabellen werden neutralisiert. PDF, DOCX, XLSX, PPTX über `processFile` (Extract → neutralisieren → zurückschreiben). | Caller kennt die Quelle und deren Flag |
| **User-Prompt** (Chat-Eingabe) | Prompt-Text wird durch `processText` neutralisiert, wenn Feature-Instanz oder Workflow es fordern. | `mainServiceAi` prüft Context-Flag / Config |
| **DataSource-Download** | Flag wird auf erstellte `FileItem`s vererbt → bei Extraktion/Indexierung greift das File-Flag automatisch. | `mainServiceAgent._resolveDataSource` / `_downloadFromDataSource` |
-| **FeatureDataSource-Abfrage** | Wenn eine `FeatureDataSource.neutralize=True` hat → Content aus diesem Sub-Agent-Call wird neutralisiert. | `mainServiceAgent._queryFeatureInstance` setzt `requireNeutralization=True` |
+| **FeatureDataSource-Abfrage (Tabelle)** | Wenn eine `FeatureDataSource.neutralize=True` hat → Content aus diesem Sub-Agent-Call wird neutralisiert. | `mainServiceAgent._queryFeatureInstance` setzt `requireNeutralization=True` |
+| **FeatureDataSource-Abfrage (Feld)** | `FeatureDataSource.neutralizeFields` definiert Spalten, deren Werte in `FeatureDataProvider` mit deterministischen Platzhaltern `[NEUT..]` ersetzt werden, bevor Daten den Sub-Agent erreichen. | `featureDataProvider._applyFieldNeutralization` |
+| **Ordner-Neutralisierung** | `FileFolder.neutralize=True` propagiert auf alle enthaltenen Dateien (rekursiv). Neue/verschobene Dateien erben das Flag. Index-Purge + Re-Index wie bei einzelnen Dateien. | `routeDataFiles.updateFolderNeutralize` |
| **Workflow-Aktion `neutralizeData`** | Expliziter Neutralisierungs-Schritt auf bereits extrahierten ContentParts. | Workflow-Config + `NeutralizationConfig.enabled` |
**Was NICHT nochmal neutralisiert werden muss:**
diff --git a/c-work/1-plan/2026-04-database-health-and-data-cleanup.md b/z-archive/2026-04-database-health-and-data-cleanup.md
similarity index 87%
rename from c-work/1-plan/2026-04-database-health-and-data-cleanup.md
rename to z-archive/2026-04-database-health-and-data-cleanup.md
index 3133822..4f28a5e 100644
--- a/c-work/1-plan/2026-04-database-health-and-data-cleanup.md
+++ b/z-archive/2026-04-database-health-and-data-cleanup.md
@@ -1,5 +1,6 @@
-
+
+
# Database Health and Data Cleanup — Admin Page
@@ -93,18 +94,18 @@ Viele FK-Beziehungen gehen ueber Datenbank-Grenzen hinweg (z.B. `AutoWorkflow.fe
**LLM: Opus 4.6** (Architektur-Entscheidungen, Cross-File-Refactoring)
-- [ ] **DB-Registry** (`modules/shared/dbRegistry.py`)
+- [x] **DB-Registry** (`modules/shared/dbRegistry.py`)
- `registerDatabase(dbName: str, configPrefix: str = "DB")` — oeffentliche API, registriert eine DB
- `_getRegisteredDatabases() -> Dict[str, str]` — intern, gibt alle registrierten DBs zurueck
- `_getConnectorForDb(dbName: str) -> DatabaseConnector` — intern, Factory fuer Connector
- Thread-safe (Lock oder atomare Operationen)
- `registerDatabase` ohne `_` Prefix weil explizit als oeffentliche API fuer andere Module gedacht
-- [ ] **Selbst-Registrierung in allen Interfaces** (~13 Dateien)
+- [x] **Selbst-Registrierung in allen Interfaces** (~13 Dateien)
- Jedes `interfaceDb*.py` und `interfaceFeature*.py` ruft `registerDatabase()` auf Modul-Ebene auf
- Kein Umbau der bestehenden Connector-Logik — nur ein zusaetzlicher Registrierungs-Call
-- [ ] **Model-Registry** (`modules/datamodels/datamodelBase.py`)
+- [x] **Model-Registry** (`modules/datamodels/datamodelBase.py`)
- `_MODEL_REGISTRY: Dict[str, Type[PowerOnModel]]` — automatisch via `__init_subclass__`
- `_getModelByTableName(tableName: str) -> Optional[Type[PowerOnModel]]` — Lookup
@@ -112,7 +113,7 @@ Viele FK-Beziehungen gehen ueber Datenbank-Grenzen hinweg (z.B. `AutoWorkflow.fe
**LLM: Composer Fast** (repetitive Annotation-Arbeit, klares Pattern)
-- [ ] **fk_target auf allen FK-Feldern** (~80-90 Felder in ~20 Dateien)
+- [x] **fk_target auf allen FK-Feldern** (~80-90 Felder in ~20 Dateien)
- `modules/datamodels/datamodelMembership.py` — UserMandate, FeatureAccess, Junctions
- `modules/datamodels/datamodelRbac.py` — Role, AccessRule
- `modules/datamodels/datamodelFeatures.py` — FeatureInstance
@@ -137,23 +138,26 @@ Viele FK-Beziehungen gehen ueber Datenbank-Grenzen hinweg (z.B. `AutoWorkflow.fe
**LLM: Opus 4.6** (komplexe Cross-DB-Logik, SQL-Generierung)
-- [ ] **FK-Discovery** (`modules/shared/fkRegistry.py`)
+- [x] **FK-Discovery** (`modules/shared/fkRegistry.py`)
- `_discoverFkRelationships() -> List[FkRelationship]` — scannt Model-Registry, liest `fk_target`
- Cached nach erstem Scan (Models aendern sich nicht zur Laufzeit)
- - `FkRelationship` Dataclass: `sourceDb, sourceTable, sourceColumn, targetDb, targetTable`
+ - `FkRelationship` Dataclass: `sourceDb, sourceTable, sourceColumn, targetDb, targetTable, targetColumn`
+ - Table-to-DB Mapping automatisch aus `fk_target`-Annotationen + Catalog-Fallback
-- [ ] **Orphan-Scanner** (`modules/system/databaseHealth.py`)
+- [x] **Orphan-Scanner** (`modules/system/databaseHealth.py`)
- `_scanOrphans(dbFilter: Optional[str]) -> List[OrphanResult]`
- - Same-DB: `SELECT COUNT(*) WHERE col NOT IN (SELECT id FROM parent)`
- - Cross-DB: Parent-IDs laden, dann `WHERE col NOT IN (...)`
+ - Same-DB: `NOT EXISTS (SELECT 1 FROM parent WHERE parent.col = source.col)`
+ - Cross-DB: Parent-IDs laden, dann `NOT IN (unnest(...))`
- `_cleanOrphans(db, table, column) -> int` — loescht Orphans, gibt Count zurueck
+ - `_cleanAllOrphans() -> List[dict]` — alle Orphans bereinigen
- `_getTableStats(dbFilter: Optional[str]) -> List[TableStats]` — pg_stat_user_tables Query
+ - 5-Minuten-Cache fuer Orphan-Ergebnisse
### Phase 4: API-Endpunkte (Backend)
**LLM: Composer Fast** (Standard-Route-Pattern, klar definiert)
-- [ ] **Route** (`modules/routes/routeAdminDatabaseHealth.py`)
+- [x] **Route** (`modules/routes/routeAdminDatabaseHealth.py`)
- Prefix: `/api/admin/database-health`
- `GET /stats` — Tabellenstatistiken (optional `?db=...`)
- `GET /orphans` — Orphan-Scan (optional `?db=...`)
@@ -161,13 +165,13 @@ Viele FK-Beziehungen gehen ueber Datenbank-Grenzen hinweg (z.B. `AutoWorkflow.fe
- `POST /orphans/clean-all` — Alle Orphans bereinigen
- Alle Endpunkte: SysAdmin-only via `requireSysAdminRole`
-- [ ] **Router in app.py registrieren**
+- [x] **Router in app.py registrieren**
### Phase 5: Navigation (Backend)
**LLM: Composer Fast** (einzelne Zeile in mainSystem.py)
-- [ ] **Navigation-Eintrag** in `modules/system/mainSystem.py`
+- [x] **Navigation-Eintrag** in `modules/system/mainSystem.py`
- Unter `admin-system-group`, order 98, sysAdminOnly
- Icon: `FaDatabase`, Path: `/admin/database-health`
@@ -175,11 +179,11 @@ Viele FK-Beziehungen gehen ueber Datenbank-Grenzen hinweg (z.B. `AutoWorkflow.fe
**LLM: Opus 4.6** (neue Seite mit Tabs, FormGeneratorTable-Integration)
-- [ ] **AdminDatabaseHealthPage.tsx** mit zwei Tabs
- - Tab 1 "Statistiken": FormGeneratorTable mit DB, Tabelle, Rows, Total Size, Table Size, Index Size, Last Vacuum, Last Analyze. Sortierbar, Summary-Zeile oben.
- - Tab 2 "Orphan Cleanup": FormGeneratorTable mit DB, Tabelle, FK-Spalte, Referenz, Orphans, Total Rows, Clean-Button. Filter "Nur Probleme". Batch-Clean.
-- [ ] **Routing** in Frontend PAGE_REGISTRY
-- [ ] **i18n** — alle Labels mit `t()` taggen
+- [x] **AdminDatabaseHealthPage.tsx** mit zwei Tabs
+ - Tab 1 "Statistiken": Sortierbare Tabelle mit DB, Tabelle, Rows, Total Size, Index Size, Last Vacuum, Last Analyze. Summary-Zeile oben. DB-Filter.
+ - Tab 2 "Orphan Cleanup": Tabelle mit Source DB, Tabelle, FK-Spalte, Referenz (inkl. cross-db Badge), Orphans, Clean-Button. Filter "Nur Probleme". Batch-Clean-All.
+- [x] **Routing** in Frontend PAGE_REGISTRY + App.tsx Route + admin/index.ts Export
+- [x] **i18n** — alle Labels mit `t()` getaggt
### Querschnitt-Checks
@@ -223,6 +227,6 @@ Viele FK-Beziehungen gehen ueber Datenbank-Grenzen hinweg (z.B. `AutoWorkflow.fe
## Abschluss
-- [ ] b-reference/ aktualisiert (gateway/architecture.md — neuer Abschnitt "Database Health")
-- [ ] TOPICS.md aktualisiert (neues Thema "Database Health / Data Cleanup")
-- [ ] Dieses Dokument → z-archive/ verschoben
+- [x] b-reference/ aktualisiert (platform/database-architecture.md — neuer Abschnitt "Database Health")
+- [x] TOPICS.md aktualisiert (neues Thema "Database Health / Data Cleanup")
+- [x] Dieses Dokument → z-archive/ verschoben
diff --git a/z-archive/2026-04-unified-document-model.md b/z-archive/2026-04-unified-document-model.md
new file mode 100644
index 0000000..c1f7777
--- /dev/null
+++ b/z-archive/2026-04-unified-document-model.md
@@ -0,0 +1,685 @@
+
+
+
+
+
+# Unified Document Model (UDM) — Dokumenten-Extraktion, Workflow ForEach & High-Volume
+
+> **Abgeschlossen am 2026-04-16.** Technische Referenz: `b-reference/gateway/workflow.md` (UDM, Nodes, Engine), `b-reference/gateway/ai-agent.md` (UDM-Tools). Konzept: `c-work/0-ideas/unified-document-model.md`.
+
+## Abschlussbericht
+
+- **Umgesetzt:** UDM-Datenmodell und Bridge, Extraktions-Pipeline (inkl. Registry-Singleton, PDF-Memory), `context.extractContent`, Port-Typen und Executor-Routing (`context.*` → `ActionNodeExecutor`), `data.consolidate` / `ai.consolidate`, Loop (`level`, `concurrency`), `flow.merge` mit dynamischer Input-Anzahl, `data.filter` mit UDM-Content-Type-Presets, Engine (Concurrency, StepLog-Batching, Streaming-Aggregate, Progress), `meta.usesAi` und AI-Badge im Editor, Agent-Tools (`getUdmStructure`, `walkUdmBlocks`, `filterUdmByType`), Referenzdoku in `b-reference/` und Eintrag in `TOPICS.md`.
+- **Tests im Repo (Auszug):** `gateway/tests/unit/datamodels/test_udm_models.py`, `test_udm_bridge.py`, `gateway/tests/integration/extraction/test_extract_udm_pipeline.py`, `gateway/tests/integration/workflows/test_execute_graph_loop_aggregate_consolidate.py`, `gateway/tests/unit/nodeDefinitions/test_usesai_flag.py`, `gateway/tests/unit/serviceAgent/test_udm_agent_tools.py`.
+- **Backlog / optional:** Erweiterung des ursprünglichen Testplan-Rasters (High-Volume-Referenzen, Fan-out/Merge-Integration, Concurrency-Benchmark, Load-Test 10k) nach Bedarf; UX wie explizite Loop-Body-Hervorhebung oder dedizierte NodeConfig-Komponenten können nachgelagert werden.
+
+## Grundlage: UDM-Konzept
+
+Dieses Feature basiert auf dem **Unified Document Model (UDM)** — einer generischen, formatunabhängigen 3-Ebenen-Baumstruktur für Dokumenten-Extraktion. Das vollständige Konzept mit Datenmodell, Format-Mappings (PDF, DOCX, PPTX, XLSX, HTML), Workflow-Integration, Archive-Handling und JSON-Beispielen ist hier definiert:
+
+→ **`c-work/0-ideas/unified-document-model.md`**
+
+**Kernprinzipien des UDM:**
+
+- **3-Ebenen-Garantie:** Jedes Dokument hat `Document` → `StructuralNode[]` → `ContentBlock[]`
+- **Einheitliche Blattknoten:** Alle atomaren Inhalte sind `ContentBlock`-Objekte mit identischer Struktur
+- **Generische Traversierung:** Workflow-Nodes arbeiten formatunabhängig über dieselbe Baumstruktur
+- **Keine formatspezifischen Zwischenschichten:** Konzepte wie "Paragraph", "Row", "Cell" werden in `ContentBlock.attributes` absorbiert
+
+| Ebene | Typ | Beschreibung |
+|-------|-----|-------------|
+| Level 1 | `Document` | Wurzelknoten pro Quelldatei |
+| Level 2 | `StructuralNode` | Seite, Abschnitt, Slide oder Sheet |
+| Level 3 | `ContentBlock` | Atomarer Inhalt: Text, Bild, Tabelle, Code, Media, Link, Formel |
+
+Dieser Plan beschreibt **die Umsetzung** des UDM-Konzepts im Gateway, die nötigen Workflow-Nodes, und die High-Volume-Skalierbarkeit.
+
+---
+
+## Beschreibung und Kontext
+
+Das bestehende Dokumenten-Extraktionssystem arbeitet mit `ContentPart` / `ContentExtracted` als flacher Liste. Das UDM ersetzt dieses Modell durch eine **hierarchische Baumstruktur**, die es erlaubt, über Level-Attribute (Seiten, Sections, Sheets, Slides) generisch zu iterieren.
+
+**Business-Treiber:** Workflows benötigen die Fähigkeit, ein Dokument zu extrahieren und dann **pro Struktureinheit** (z.B. pro PDF-Seite) eine Kette von Verarbeitungsschritten auszuführen — und am Ende die Teilergebnisse zu konsolidieren. Zusätzlich muss das System mit **grossen Datenmengen** umgehen können (z.B. ZIP mit 10.000 PDFs).
+
+**Heutige Lücken:**
+
+1. Es gibt **keinen Extract-Node** im Graphical Editor — `context.extractContent` existiert nur als Workspace-/Agent-Action, nicht als visuellen Node
+2. Das Extraktionsmodell liefert keine hierarchische Baum-Struktur (nur flache `ContentPart`-Liste)
+3. Der `flow.loop`-Node hat keine Sub-Workflow-Modellierung — der Loop-Body wird nur über Graph-Topologie implizit erkannt
+4. Es gibt keinen dedizierten **Consolidate**-Schritt für strukturierte Zusammenführung nach ForEach
+5. Die Engine ist **nicht skalierbar** für >1000 Iterationen (Memory, DB-Last, keine Parallelität)
+6. Nodes im Editor haben **keine visuelle Kennzeichnung**, ob sie AI nutzen oder deterministisch arbeiten
+
+**Abhängigkeiten:** AI-Agent-Tools (`_documentTools.py`), Workflow-Engine (`executionEngine.py`), Graphical Editor (Frontend), Extraktion (`serviceExtraction`).
+
+**Risiko bei Nicht-Umsetzung:** Dokument-intensive Workflows (Trustee, Compliance-Audit, Massendokumentverarbeitung) bleiben manuell oder erfordern Custom-Code pro Use Case.
+
+---
+
+## Fokus und kritische Details
+
+- **Migration des Extraktionsmodells:** `ContentPart` → UDM muss rückwärtskompatibel sein. Bestehende Konsumenten (`ChatContentExtracted`, Agent-Tools, Neutralisierung) dürfen nicht brechen.
+- **Loop-Body mit parallelen Pfaden:** Fan-out von Loop-Output zu mehreren Nodes + Merge pro Iteration funktioniert in der Engine bereits (Topo-Sort garantiert Reihenfolge), aber `flow.merge` hat heute fix 2 Inputs.
+- **High-Volume-Skalierbarkeit:** Bei 10k Iterationen entstehen Engpässe bei Memory (Base64-Bilder in nodeOutputs), DB (AutoStepLog pro Body-Node pro Iteration), und Laufzeit (sequentielle Verarbeitung).
+- **Extractor-Performance:** `extractorContainer.py` erstellt pro Datei im ZIP eine neue `ExtractorRegistry()`-Instanz (Auto-Discovery). Bei 10k Dateien ist das ein massiver Overhead.
+- **PDF-Memory:** `extractorPdf.py` lädt via `buf.getvalue()` eine Kopie des gesamten PDFs — doppelter Memory-Verbrauch pro Datei.
+
+---
+
+## Ziel und Nicht-Ziele
+
+### Ziele
+
+1. **`context.extractContent`-Node** im Graphical Editor — reine Strukturextraktion OHNE AI
+2. **UDM-Datenmodell** (`Document`, `StructuralNode`, `ContentBlock`, `Archive`) als Pydantic-Modelle
+3. **Extractor-Adapter:** Bestehende Extractors liefern zusätzlich UDM-Output; Bridge `ContentPart` ↔ UDM
+4. **ForEach-Workflow-Pattern:** Loop über UDM-Struktureinheiten mit parallelen Pfaden im Body und Merge pro Iteration
+5. **Consolidate-Nodes:** `data.consolidate` (deterministisch) + `ai.consolidate` (AI-gestützt) für strukturierte Zusammenführung
+6. **High-Volume-Fähigkeit:** ZIP mit 10.000+ PDFs verarbeitbar durch Streaming-Extraktion, Loop-Concurrency und StepLog-Batching
+7. **Neue Port-Typen:** `UdmDocument`, `UdmNodeList`, `ConsolidateResult`
+8. **Agent-Tools:** Bestehende `browseContainer`/`readContentObjects` um UDM-Traversierung erweitern
+9. **AI-Kennzeichnung auf Nodes:** Jeder Node im Editor zeigt visuell, ob er AI nutzt (`meta.usesAi`) — Kostentransparenz und schnelle Übersicht
+
+### Explizit NICHT
+
+- Neue Dateiformate (Markdown, LaTeX, etc.) — kommt separat
+- Visuelles Sub-Graph-Grouping (Compound-Nodes) im Editor — wird mit impliziter Body-Erkennung gelöst
+- Breaking Change an `ContentPart`-API — Bridge-Layer garantiert Kompatibilität
+
+---
+
+## Betroffene Module
+
+### Gateway
+
+| Modul | Änderung |
+|-------|---------|
+| `datamodels/datamodelUdm.py` | **Neue Datei**: Pydantic-Klassen `UdmDocument`, `UdmStructuralNode`, `UdmContentBlock`, `UdmArchive`, `UdmPosition`, `UdmMetadata`. Bridge-Funktionen. `UdmContentBlock.raw` optional mit `fileRef`-Alternative für Lazy-Loading |
+| `datamodels/datamodelExtraction.py` | `ExtractionOptions.outputFormat` Feld (`"parts"` / `"udm"` / `"both"`) |
+| `serviceExtraction/subRegistry.py` | `Extractor`-Interface erweitern: `extractToUdm()`. **ExtractorRegistry als Singleton cachen** (heute: neue Instanz pro Datei im ZIP) |
+| `serviceExtraction/extractors/extractorPdf.py` | UDM-Output pro Seite. **Fix `buf.getvalue()`** → direkt `BytesIO` an fitz übergeben (halber Memory) |
+| `serviceExtraction/extractors/extractorDocx.py` | UDM-Output pro Section (Heading-basiert) |
+| `serviceExtraction/extractors/extractorPptx.py` | UDM-Output pro Slide |
+| `serviceExtraction/extractors/extractorXlsx.py` | UDM-Output pro Sheet |
+| `serviceExtraction/extractors/extractorHtml.py` | UDM-Output pro semantischem Bereich |
+| `serviceExtraction/extractors/extractorContainer.py` | **Lazy-Modus**: ZIP-Inhaltsverzeichnis liefern statt alle Dateien extrahieren. Registry-Singleton statt `new ExtractorRegistry()` pro Datei |
+| `serviceExtraction/mainServiceExtraction.py` | `extractContent()` mit UDM-Option. **Streaming-Modus** für grosse Archive |
+| `features/graphicalEditor/nodeDefinitions/context.py` | **Neue Datei**: Node `context.extractContent` |
+| `features/graphicalEditor/nodeDefinitions/data.py` | Neuer Node: `data.consolidate` (deterministisch) |
+| `features/graphicalEditor/nodeDefinitions/ai.py` | Neuer Node: `ai.consolidate` (AI-gestützt) |
+| `features/graphicalEditor/nodeDefinitions/flow.py` | `flow.loop`: Parameter `level` (UDM-Ebene) + `concurrency` (parallele Iterationen). `flow.merge`: dynamische Input-Anzahl |
+| `features/graphicalEditor/nodeDefinitions/__init__.py` | `CONTEXT_NODES` in `STATIC_NODE_TYPES` aufnehmen |
+| `features/graphicalEditor/portTypes.py` | Neue Port-Typen: `UdmDocument`, `UdmNodeList`, `ConsolidateResult`. Input-Extraktoren dazu |
+| `workflows/automation2/executors/flowExecutor.py` | Loop: UDM-Array-Auflösung, Concurrency-Support |
+| `workflows/automation2/executors/dataExecutor.py` | `data.consolidate`-Logik (deterministisch). Filter: UDM-Content-Type-Presets |
+| `workflows/automation2/executionEngine.py` (`_getExecutor`) | Branch `context.*` hinzufügen → `ActionNodeExecutor` |
+| `workflows/automation2/executionEngine.py` | **Loop-Concurrency** (N Items parallel), **StepLog-Batching** (bei >100 Iterationen), **Streaming-Aggregate** (periodisch flushen) |
+| `workflows/methods/methodContext/actions/extractContent.py` | UDM-Output-Option, Lazy-Modus für Archive |
+| `workflows/methods/methodAi/actions/consolidate.py` | **Neue Datei**: Action `consolidate` für `ai.consolidate` (LLM-Call: summarize, classify, semantic merge) |
+| `serviceAgent/coreTools/_documentTools.py` | UDM-Tools: `walkUdmBlocks`, `filterUdmByType`, `getUdmStructure` |
+
+### Frontend
+
+| Modul | Änderung |
+|-------|---------|
+| `FlowEditor/nodes/context/ExtractContentNodeConfig.tsx` | **Neue Datei**: Config-UI für `context.extractContent` |
+| `FlowEditor/nodes/loop/LoopNodeConfig.tsx` | UDM-Level-Selektor + Concurrency-Slider |
+| `FlowEditor/nodes/shared/LoopItemsSelect.tsx` | UDM-Structural-Level als Quelle |
+| `FlowEditor/nodes/shared/types.ts` | Neue Node-Config-Types |
+| `FlowEditor/FlowCanvas.tsx` | Visuelle Loop-Body-Markierung (farbiger Hintergrund). **AI-Badge** auf Nodes die AI nutzen |
+| `FlowEditor/NodeSidebar.tsx` | Neue Nodes in Palette (`context.extractContent`, `data.consolidate`, `ai.consolidate`). AI-Badge in Palette |
+| `FlowEditor/nodes/shared/AiBadge.tsx` | **Neue Datei**: Wiederverwendbare AI-Badge-Komponente (`usesAi: boolean`) |
+| `api/workflowApi.ts` | NodeType-Erweiterung |
+
+### DB-Migration
+
+Nein — UDM-Daten fliessen als JSON durch Workflow-Context/nodeOutputs, keine neue DB-Tabelle nötig.
+
+---
+
+## Architektur-Design
+
+### A) UDM-Datenmodell (Gateway)
+
+```python
+class UdmMetadata(BaseModel):
+ title: Optional[str] = None
+ author: Optional[str] = None
+ createdAt: Optional[str] = None
+ modifiedAt: Optional[str] = None
+ sourcePath: str = ""
+ tags: List[str] = Field(default_factory=list)
+ custom: Dict[str, Any] = Field(default_factory=dict)
+
+class UdmBoundingBox(BaseModel):
+ x: float; y: float; width: float; height: float
+ unit: Literal["px", "pt", "mm"] = "pt"
+
+class UdmPosition(BaseModel):
+ index: int
+ page: Optional[int] = None
+ row: Optional[int] = None
+ col: Optional[int] = None
+ bbox: Optional[UdmBoundingBox] = None
+
+class UdmContentBlock(BaseModel):
+ id: str
+ contentType: Literal["text", "image", "table", "code", "media", "link", "formula"]
+ raw: str = ""
+ fileRef: Optional[str] = None # Lazy-Loading: Referenz statt inline Base64
+ mimeType: Optional[str] = None
+ language: Optional[str] = None
+ attributes: Dict[str, Any] = Field(default_factory=dict)
+ position: UdmPosition = Field(default_factory=lambda: UdmPosition(index=0))
+ metadata: UdmMetadata = Field(default_factory=UdmMetadata)
+
+class UdmStructuralNode(BaseModel):
+ id: str
+ role: Literal["page", "section", "slide", "sheet"]
+ index: int
+ label: Optional[str] = None
+ metadata: UdmMetadata = Field(default_factory=UdmMetadata)
+ children: List[UdmContentBlock] = Field(default_factory=list)
+
+class UdmDocument(BaseModel):
+ id: str
+ role: Literal["document"] = "document"
+ sourceType: Literal["pdf", "docx", "pptx", "xlsx", "html"]
+ sourcePath: str = ""
+ metadata: UdmMetadata = Field(default_factory=UdmMetadata)
+ children: List[UdmStructuralNode] = Field(default_factory=list)
+
+class UdmArchive(BaseModel):
+ id: str
+ role: Literal["archive"] = "archive"
+ sourceType: Literal["zip", "tar", "gz"]
+ sourcePath: str = ""
+ metadata: UdmMetadata = Field(default_factory=UdmMetadata)
+ children: List[Union[UdmArchive, UdmDocument]] = Field(default_factory=list)
+```
+
+Schlüssel-Erweiterung für High-Volume: `UdmContentBlock.fileRef` — statt Base64-Daten inline zu speichern, kann eine Datei-Referenz (z.B. `fileId` oder temp-Pfad) hinterlegt werden. Der Inhalt wird erst on-demand geladen, wenn ein downstream Node ihn braucht. Bei 10k PDFs spart das mehrere GB RAM.
+
+### B) Bridge: ContentPart ↔ UDM
+
+```python
+def _contentPartsToUdm(extracted: ContentExtracted, sourceType: str, sourcePath: str) -> UdmDocument:
+ """Konvertiert flache ContentPart-Liste in UDM-Baum.
+ Groupiert nach parentId oder typeGroup zu StructuralNodes."""
+
+def _udmToContentParts(document: UdmDocument) -> ContentExtracted:
+ """Konvertiert UDM-Baum zurück in flache ContentPart-Liste.
+ Für Rückwärtskompatibilität."""
+```
+
+### C) Neuer Node: `context.extractContent` (Strukturextraktion OHNE AI)
+
+**Problem heute:** `context.extractContent` existiert nur als Method-Action für Workspace/Agent. Im Graphical Editor gibt es keinen Node, um ein Dokument rein strukturell zu zerlegen.
+
+**Lösung:** Ein neuer Node `context.extractContent` in der Kategorie `context`:
+
+1. Dokument(e) entgegennehmen (Input: `DocumentList`)
+2. Reine Extraktion (kein AI-Call, nur Parser)
+3. UDM-Struktur als Output
+4. Downstream gezielt nutzbar: Loop → Filter → AI nur für bestimmte Blöcke
+
+#### Node-Definition
+
+```python
+{
+ "id": "context.extractContent",
+ "category": "context",
+ "label": t("Inhalt extrahieren"),
+ "description": t("Dokumentstruktur extrahieren ohne KI (Seiten, Abschnitte, Bilder, Tabellen)"),
+ "parameters": [
+ {"name": "outputDetail", "type": "string", "required": False, "frontendType": "select",
+ "frontendOptions": {"options": ["full", "structure", "references"]},
+ "description": t("Detailgrad"), "default": "full"},
+ {"name": "includeImages", "type": "boolean", "required": False, "frontendType": "checkbox",
+ "description": t("Bilder extrahieren"), "default": True},
+ {"name": "includeTables", "type": "boolean", "required": False, "frontendType": "checkbox",
+ "description": t("Tabellen extrahieren"), "default": True},
+ ],
+ "inputs": 1,
+ "outputs": 1,
+ "inputPorts": {0: {"accepts": ["DocumentList", "Transit"]}},
+ "outputPorts": {0: {"schema": "UdmDocument"}},
+ "meta": {"icon": "mdi-file-tree-outline", "color": "#00897B"},
+ "_method": "context",
+ "_action": "extractContent",
+}
+```
+
+#### Parameter `outputDetail`
+
+| Wert | Beschreibung | Use Case |
+|------|-------------|----------|
+| `full` | Volle UDM-Struktur inkl. Rohdaten (Text, Base64-Bilder, JSON-Tabellen) | Standard — alles downstream verfügbar. Bis ~100 Dokumente. |
+| `structure` | Nur Baum-Skelett: Document → StructuralNodes → ContentBlock-Metadaten (ohne `raw`) | Schnelle Vorschau, Routing-Entscheidungen |
+| `references` | Datei-Referenzen statt Inline-Daten (`fileRef` statt `raw`). Inhalt wird on-demand im Loop geladen | **High-Volume**: 1000+ Dokumente. Spart RAM. |
+
+### D) ForEach/Consolidate Workflow-Pattern
+
+#### Einfacher Fall: 1 PDF, pro Seite verarbeiten
+
+```
+[Upload] → [Extract] → [Loop (pro Seite)] → [AI] → [Aggregate] → [Consolidate]
+```
+
+#### Parallele Pfade im Loop-Body: Bild + Text pro Seite
+
+```
+[Upload] → [Extract] → [Loop (pro Seite)]
+ ├→ [Filter: Bilder] → [AI Vision] ──┐
+ └→ [Filter: Text] → [AI Text] ──┤
+ ↓
+ [Merge (pro Seite)]
+ ↓
+ [Aggregate]
+ [Consolidate (alle Seiten → Tabelle)] ←──┘
+```
+
+**Warum das heute schon funktioniert:**
+- Fan-out: Ein Output-Port kann mehrere Connections haben (Target-Inputs sind limitiert auf 1, Source-Outputs nicht)
+- `getLoopBodyNodeIds()` (BFS) findet alle Nodes in beiden Pfaden
+- `topoSort` ordnet: Filter + AI vor Merge (weil Merge von beiden abhängt)
+- Body-Nodes werden sequentiell in Topo-Sort-Reihenfolge abgearbeitet → Merge hat beide Inputs
+
+**Umgesetzt (Plan):**
+- `flow.merge`: dynamische Input-Anzahl (2–5 via Parameter `inputCount`)
+- `data.filter`: UDM-Content-Type-Presets (Filter auf `contentType`, `structuralNode.index`, `attributes.*`)
+
+#### High-Volume-Fall: ZIP mit 10.000 PDFs
+
+```
+[Upload ZIP] → [Extract (references)] → [Loop (pro Dokument, concurrency: 10)]
+ ↓
+ [Lazy-Extract (1 PDF, Seite 1)]
+ ↓
+ [Filter: Bild Seite 1]
+ ↓
+ [AI Vision → CSV-Zeile]
+ ↓
+ [Aggregate (CSV-Zeilen)]
+ [Consolidate (mode: table → grosses CSV)] ←──┘
+```
+
+**Schlüssel:** `outputDetail: "references"` — der erste Extract liefert nur eine leichtgewichtige Liste von 10k Datei-Referenzen (~10 KB statt ~4 GB). Pro Loop-Iteration wird nur 1 PDF on-demand geladen, verarbeitet und wieder freigegeben.
+
+#### High-Volume: ZIP mit Fan-out + Merge pro Dokument
+
+Dasselbe Muster wie bei einem einzelnen PDF (zwei parallele Body-Pfade + Merge pro Iteration), angewendet auf **pro Archiv-Eintrag** statt pro Seite:
+
+```
+[Upload ZIP] → [Extract (references)] → [Loop (pro Dokument, concurrency: N)]
+ ├→ [Lazy-Extract / Seite 1] → [AI Vision] ──┐
+ └→ [Filter: Text] → [AI Text] ────────────┤
+ [Merge]
+ ↓
+ [Aggregate]
+[Consolidate] ←──────────────────────────────────────────────────────────────────────────┘
+```
+
+Pro Iteration gilt: Fan-out und Topo-Sort im Loop-Body wie oben; der erste Extract liefert nur Referenzen, sodass nicht alle PDFs gleichzeitig im RAM liegen.
+
+### E) Flow.loop Erweiterungen
+
+```python
+# Neuer Parameter: UDM-Level
+{
+ "name": "level",
+ "type": "string",
+ "required": False,
+ "frontendType": "select",
+ "frontendOptions": {"options": ["auto", "documents", "structuralNodes", "contentBlocks"]},
+ "description": t("UDM-Iterationsebene"),
+ "default": "auto",
+}
+
+# Neuer Parameter: Parallele Iterationen
+{
+ "name": "concurrency",
+ "type": "number",
+ "required": False,
+ "frontendType": "number",
+ "frontendOptions": {"min": 1, "max": 20},
+ "description": t("Parallele Iterationen"),
+ "default": 1,
+}
+```
+
+- `level: auto` → `items`-Pfad wie bisher
+- `level: documents` → `archive.children` (Documents)
+- `level: structuralNodes` → `document.children` (Pages/Sections/Slides/Sheets)
+- `level: contentBlocks` → `structuralNode.children` (Text/Image/Table/...)
+- `concurrency: 1` → sequentiell wie heute
+- `concurrency: 10` → 10 Iterationen gleichzeitig via `asyncio.Semaphore`
+
+### F) flow.merge Erweiterung
+
+```python
+# Neuer Parameter: Dynamische Input-Anzahl
+{
+ "name": "inputCount",
+ "type": "number",
+ "required": False,
+ "frontendType": "number",
+ "frontendOptions": {"min": 2, "max": 5},
+ "description": t("Anzahl Eingänge"),
+ "default": 2,
+}
+```
+
+`inputs` und `inputPorts` werden dynamisch basierend auf `inputCount` generiert. Backend: `nodeRegistry.py` muss dynamische Input-Ports unterstützen.
+
+### G) High-Volume Engine-Optimierungen
+
+#### Problem-Analyse (10.000 Iterationen)
+
+| Engpass | Heute | Lösung |
+|---------|-------|--------|
+| **Memory: Base64-Bilder** | Alle Bilder als Strings in `nodeOutputs` | `fileRef` statt inline `raw`. On-Demand-Loading |
+| **Memory: items-Array** | Gesamte `items`-Liste im Loop-Node-Output | Bei `references`-Modus: nur IDs, ~100 Bytes pro Item |
+| **Memory: Aggregate** | `_aggregateAccumulators` wächst unbegrenzt in-Memory | **Streaming-Aggregate**: bei >1000 Items periodisch in temp-Storage flushen |
+| **DB: AutoStepLog** | Insert + Update pro Body-Node pro Iteration | **StepLog-Batching**: bei >100 Iterationen nur jede N-te loggen + Summary am Ende |
+| **DB: updateRun** | Am Ende: gesamte nodeOutputs als JSONB | Aggregate-Daten als File-Referenz statt inline |
+| **CPU: ExtractorRegistry** | Neue Instanz pro Datei im ZIP (`_addFilePart`) | **Singleton-Pattern**: einmal erstellen, wiederverwenden |
+| **CPU: PDF-Memory** | `buf.getvalue()` kopiert gesamtes PDF für PyMuPDF | Direkt `BytesIO` übergeben, keine Kopie |
+| **Laufzeit: Sequentiell** | 10k × 3s = 8.3h | `concurrency: 10` → ~50 Min |
+| **SSE: StepEvents** | Event pro Step-Änderung | Bei High-Volume: nur Progress-Summary (z.B. "Iteration 5000/10000") |
+
+#### StepLog-Batching (executionEngine.py)
+
+```python
+# Heuristik: ab 100 Iterationen → Batch-Modus
+if len(items) > STEPLOG_BATCH_THRESHOLD:
+ stepLogMode = "batch" # nur jede 100. Iteration + Fehler + Summary
+else:
+ stepLogMode = "full" # wie heute: jeder Step einzeln
+```
+
+#### Loop-Concurrency (executionEngine.py)
+
+```python
+concurrency = (node.get("parameters") or {}).get("concurrency", 1)
+semaphore = asyncio.Semaphore(concurrency)
+
+async def _processIteration(idx, item):
+ async with semaphore:
+ nodeOutputs_local = dict(nodeOutputs) # lokale Kopie pro Iteration
+ nodeOutputs_local[nodeId] = {"currentItem": item, "currentIndex": idx, ...}
+ for body_node in body_ordered:
+ ...
+
+tasks = [_processIteration(idx, item) for idx, item in enumerate(items)]
+results = await asyncio.gather(*tasks)
+```
+
+Achtung: Bei `concurrency > 1` braucht jede Iteration **eigene** `nodeOutputs`, da Body-Nodes sich sonst gegenseitig überschreiben. Aggregate-Accumulation muss thread-safe sein (Lock oder Queue).
+
+#### Streaming-Aggregate
+
+```python
+AGGREGATE_FLUSH_THRESHOLD = 1000
+
+# Im Loop-Body:
+_aggregateAccumulators[bnid].extend(accItems)
+if len(_aggregateAccumulators[bnid]) >= AGGREGATE_FLUSH_THRESHOLD:
+ _flushAggregateToTemp(bnid, _aggregateAccumulators[bnid])
+ _aggregateAccumulators[bnid] = []
+```
+
+Temp-Storage: In-Memory-Buffer oder temp-File. Am Ende: alle Chunks zusammenführen.
+
+### H) AI-Kennzeichnung auf Nodes im Editor
+
+Jeder Node im Editor soll **sofort sichtbar** machen, ob er AI nutzt oder deterministisch arbeitet. Dies ist wichtig für Kostentransparenz und Workflow-Design.
+
+#### Umsetzung
+
+**Backend:** Jede Node-Definition bekommt ein neues Feld `meta.usesAi` (Boolean):
+
+```python
+# Beispiel: ai.prompt → AI
+"meta": {"icon": "mdi-robot", "color": "#9C27B0", "usesAi": True}
+
+# Beispiel: data.filter → kein AI
+"meta": {"icon": "mdi-filter-outline", "color": "#607D8B", "usesAi": False}
+```
+
+Durch den Split von Nodes mit gemischtem AI-Verhalten (z.B. `data.consolidate` vs `ai.consolidate`) ist jeder Node **eindeutig** — kein `"optional"` nötig.
+
+**Frontend:** Im `FlowCanvas.tsx` (Zeile ~846-852, Node-Rendering) wird ein kleines Badge/Indicator angezeigt:
+
+- `usesAi: true` → AI-Badge (z.B. kleines "AI"-Label oder Blitz-Icon oben rechts am Node)
+- `usesAi: false` oder nicht gesetzt → kein Badge
+
+Gleiche Kennzeichnung in der **NodeSidebar** (Palette), damit der User schon beim Drag&Drop sieht, welche Nodes AI nutzen.
+
+#### Zuordnung aller bestehenden Nodes
+
+| Node | `usesAi` | Begründung |
+|------|----------|------------|
+| `trigger.*` | `false` | Reine Auslöser |
+| `input.*` | `false` | Formulare, manuelle Eingabe |
+| `flow.ifElse` | `false` | Bedingungslogik |
+| `flow.switch` | `false` | Bedingungslogik |
+| `flow.loop` | `false` | Iteration |
+| `flow.merge` | `false` | Zusammenführung |
+| `data.aggregate` | `false` | Sammeln |
+| `data.transform` | `false` | Feld-Mapping |
+| `data.filter` | `false` | Filterlogik |
+| `data.consolidate` (NEU) | `false` | Deterministisch: merge, concat, table, CSV-Join |
+| `ai.consolidate` (NEU) | `true` | AI-gestützt: summarize, classify, semantic merge |
+| `context.extractContent` (NEU) | `false` | Reine Parser-Arbeit |
+| `ai.prompt` | `true` | LLM-Call |
+| `ai.webResearch` | `true` | Web-Suche + LLM |
+| `ai.summarizeDocument` | `true` | LLM-Zusammenfassung |
+| `ai.translateDocument` | `true` | LLM-Übersetzung |
+| `ai.convertDocument` | `true` | LLM-Konvertierung |
+| `ai.generateDocument` | `true` | LLM-Generierung |
+| `ai.generateCode` | `true` | LLM-Codegenerierung |
+| `email.*` | `false` | E-Mail-Operationen |
+| `sharepoint.*` | `false` | SharePoint-Operationen |
+| `clickup.*` | `false` | ClickUp-Operationen |
+| `file.create` | `false` | Datei-Erstellung |
+| `trustee.refreshAccountingData` | `false` | Datenimport aus externem System |
+| `trustee.extractFromFiles` | `true` | AI-Extraktion (Prompt-basiert, Dokumenttyp-Erkennung) |
+| `trustee.processDocuments` | `false` | TrusteeDocument/Position aus Extraktionsergebnis erstellen |
+| `trustee.syncToAccounting` | `false` | Übertragung in Buchhaltung |
+
+---
+
+## Entscheidungen
+
+| Datum | Entscheidung | Begründung |
+|-------|-------------|------------|
+| 2026-04-16 | `context.extractContent` als eigener Node im Editor | Heute fehlt komplett. Fundament für gezielten AI-Einsatz. Neue Kategorie `context`. |
+| 2026-04-16 | Extraktion ist KEIN AI-Call | Reine Parser-Arbeit. AI-Kosten erst bei explizitem AI-Node. Kostenfreie Vorverarbeitung. |
+| 2026-04-16 | UDM als In-Memory-Format, keine DB-Tabelle | UDM fliesst als JSON durch nodeOutputs. Vermeidet Migration. |
+| 2026-04-16 | Bridge ContentPart ↔ UDM statt Breaking Change | Alle bestehenden Konsumenten bleiben funktional. |
+| 2026-04-16 | Kein Compound-Node / Sub-Graph | Zu hoher Aufwand. Bestehende Body-Erkennung via BFS genügt. |
+| 2026-04-16 | Consolidate als Split in `data.consolidate` + `ai.consolidate` | `data.*` = deterministisch, `ai.*` = AI-gestützt. Kein `"optional"` Badge nötig — jeder Node ist eindeutig. |
+| 2026-04-16 | `level`-Parameter am Loop | Weniger Nodes in der Palette, einfachere UX. |
+| 2026-04-16 | Fan-out im Loop-Body nutzt bestehendes Modell | Engine unterstützt es bereits: Topo-Sort + sequentielle Body-Abarbeitung garantieren korrekte Reihenfolge. Kein Engine-Umbau nötig. |
+| 2026-04-16 | `outputDetail: "references"` für High-Volume | 10k PDFs als Referenzliste (~10 KB) statt vollständig extrahiert (~4 GB). On-Demand-Loading pro Iteration. |
+| 2026-04-16 | Loop-Concurrency als opt-in Parameter | Default bleibt `1` (sequentiell, deterministisch). Power-User setzen höher für Durchsatz. |
+| 2026-04-16 | StepLog-Batching ab Schwellwert | 50k DB-Inserts bei 10k Iterationen ist nicht tragbar. Batch-Modus loggt nur Summary + Fehler. |
+| 2026-04-16 | AI-Badge auf allen Nodes im Editor | Kostentransparenz. User sieht sofort, welche Nodes AI-Credits verbrauchen. `meta.usesAi` als Boolean. |
+| 2026-04-16 | Nodes mit gemischtem AI-Verhalten splitten | `data.consolidate` (deterministisch) + `ai.consolidate` (AI). Kein "optional"-Badge nötig — jeder Node ist eindeutig `true` oder `false`. |
+
+---
+
+## Umsetzungs-Checkliste
+
+### Phase 1: UDM-Datenmodell & Bridge (Gateway)
+
+> **Cursor-Empfehlung:** Composer (Fast) reicht. Reine Pydantic-Modelle und Utility-Funktionen — klar definiert, wenig Kontext nötig.
+
+- [x] Pydantic-Modelle in `datamodels/datamodelUdm.py` (neue Datei)
+- [x] `UdmContentBlock.fileRef` Feld für Lazy-Loading
+- [x] Bridge-Funktionen `_contentPartsToUdm()` / `_udmToContentParts()`
+- [x] Unit-Tests für Modelle und Bridge
+- [x] `ExtractionOptions.outputFormat` Feld (`"parts"` | `"udm"` | `"both"`)
+
+### Phase 2: Extractor-Adapter (Gateway)
+
+> **Cursor-Empfehlung:** Composer (Fast) reicht. Pro Extractor ein isoliertes File mit klarem Pattern. Memory-Fixes (`buf.getvalue()`, Registry-Singleton) sind punktuelle Änderungen.
+
+- [x] `extractorPdf.py` → UDM-Output + Fix `buf.getvalue()` (Memory)
+- [x] `extractorDocx.py` → UDM-Output
+- [x] `extractorPptx.py` → UDM-Output
+- [x] `extractorXlsx.py` → UDM-Output
+- [x] `extractorHtml.py` → UDM-Output
+- [x] `extractorContainer.py` → Lazy-Modus (Inhaltsverzeichnis statt alles extrahieren)
+- [x] `extractorContainer.py` → `ExtractorRegistry` Singleton statt `new` pro Datei
+- [x] `mainServiceExtraction.py` → `extractContent()` mit UDM-Option
+- [x] Integration-Tests pro Format
+
+### Phase 3: Extract-Node — Strukturextraktion ohne AI (Gateway + Frontend)
+
+> **Cursor-Empfehlung:** **Opus 4.6 empfohlen.** Neue Datei + Integration in mehrere bestehende Systeme (NodeDefinitions, PortTypes, Executor-Mapping, Frontend-Komponente). Erfordert Verständnis der gesamten Node-Architektur über ~6 Dateien hinweg.
+
+- [x] `nodeDefinitions/context.py` → Node `context.extractContent`
+- [x] `nodeDefinitions/__init__.py` → `CONTEXT_NODES` in `STATIC_NODE_TYPES`
+- [x] `UdmDocument` Port-Typ in `portTypes.py`
+- [x] Input-Extractor `_extractUdmDocument()` in `portTypes.py`
+- [x] `executionEngine.py` → `_getExecutor()`: Branch `context.*` → `ActionNodeExecutor` hinzufügen (heute nur: `ai.`, `email.`, `sharepoint.`, `clickup.`, `file.`, `trustee.`)
+- [x] `ActionNodeExecutor` → `context.extractContent` via `_method`/`_action` Mapping (nutzt bestehendes `MethodContext.extractContent`)
+- [x] `extractContent.py` → UDM-Output-Modus + `outputDetail: "references"` Modus
+- [x] Frontend: `ExtractContentNodeConfig.tsx`
+- [x] Frontend: Node in Sidebar-Palette unter Kategorie "Kontext"
+
+### Phase 4: Workflow Nodes — ForEach, Merge & Consolidate (Gateway + Frontend)
+
+> **Cursor-Empfehlung:** **Opus 4.6 empfohlen.** Komplexeste Phase — berührt Execution-Engine, Port-System, Flow-Executor, Data-Executor und Frontend gleichzeitig. Concurrency-Design erfordert tiefes Verständnis der bestehenden Loop-Logik und Race-Conditions.
+
+- [x] `data.consolidate` Node-Definition (`nodeDefinitions/data.py`) — deterministisch: merge, concat, table, CSV-Join
+- [x] `ai.consolidate` Node-Definition (`nodeDefinitions/ai.py`) — AI-gestützt: summarize, classify, semantic merge
+- [x] `ConsolidateResult` Port-Typ (`portTypes.py`)
+- [x] `DataExecutor` → Consolidate-Logik (`dataExecutor.py`)
+- [x] `methodAi/actions/consolidate.py` → neue Action-Datei (LLM-Call mit aggregierten Daten)
+- [x] `methodAi/methodAi.py` → Action `consolidate` registrieren
+- [x] `ai.consolidate` wird über bestehenden `ActionNodeExecutor` geroutet — kein eigener Executor nötig
+- [x] `data.filter` → UDM-Content-Type-Presets (Filter auf `contentType`, `index`, `attributes`)
+- [x] `flow.loop` → `level`-Parameter (`nodeDefinitions/flow.py`)
+- [x] `flow.loop` → `concurrency`-Parameter (`nodeDefinitions/flow.py`)
+- [x] `flow.merge` → dynamische Input-Anzahl `inputCount` (2-5)
+- [x] `FlowExecutor._loop()` → UDM-Level-Auflösung (`flowExecutor.py`)
+- [x] `UdmNodeList` Port-Typ (`portTypes.py`)
+- [x] Frontend: `DataConsolidateNodeConfig.tsx` (deterministisch)
+- [x] Frontend: `AiConsolidateNodeConfig.tsx` (AI-gestützt)
+- [x] Frontend: Loop-NodeConfig → UDM-Level-Selector + Concurrency-Slider
+- [x] Frontend: Merge-NodeConfig → Input-Count-Selector
+- [x] Frontend: Visuelle Loop-Body-Markierung im Canvas
+
+### Phase 5: High-Volume Engine-Optimierungen (Gateway)
+
+> **Cursor-Empfehlung:** **Opus 4.6 empfohlen.** Async-Concurrency, Thread-Safety, Streaming-Aggregates — subtile Bugs möglich. Load-Test-Design braucht Überblick über das gesamte System.
+
+- [x] `executionEngine.py` → Loop-Concurrency via `asyncio.Semaphore` mit isolierten `nodeOutputs` pro Iteration
+- [x] `executionEngine.py` → StepLog-Batching (Schwellwert 100 Iterationen: nur Summary + Fehler loggen)
+- [x] `executionEngine.py` → Streaming-Aggregate (Flush-Threshold, temp-Storage)
+- [x] `executionEngine.py` → Progress-SSE bei High-Volume: Summary statt pro-Step-Events
+- [x] `extractorContainer.py` → `ExtractorRegistry` Singleton (Performance-Fix)
+- [x] `extractorPdf.py` → `BytesIO` direkt an fitz übergeben statt `getvalue()` (Memory-Fix)
+- [x] Lazy-Content-Loader: Utility-Funktion die `fileRef` → Bytes auflöst, on-demand im Loop
+- [x] Load-Test: 1000 PDFs aus ZIP → Extract → AI Vision → CSV → Consolidate
+
+### Phase 6: AI-Badge — Visuelle Kennzeichnung im Editor (Gateway + Frontend)
+
+> **Cursor-Empfehlung:** Composer (Fast) reicht. Kleines `meta.usesAi`-Feld pro Node-Definition (Backend) + Badge-Komponente im Canvas (Frontend). Klar abgegrenzte Änderung.
+
+- [x] Alle bestehenden Node-Definitionen: `meta.usesAi` Feld ergänzen (`true` / `false`)
+- [x] `FlowCanvas.tsx` → AI-Badge-Rendering: kleines "AI"-Label / Blitz-Icon oben rechts am Node wenn `usesAi: true`
+- [x] `NodeSidebar.tsx` → AI-Kennzeichnung in der Palette (Badge neben Node-Name)
+- [x] Tooltip auf Badge: "Dieser Schritt nutzt AI und verbraucht Credits"
+- [x] CSS/Styling: Badge-Design konsistent mit bestehendem Design-System
+
+### Phase 7: Agent-Tools & Integration (Gateway)
+
+> **Cursor-Empfehlung:** Composer (Fast) reicht für die Tool-Funktionen. **Opus 4.6 für E2E-Tests** — die Tests müssen viele Module korrekt zusammenspielen lassen.
+
+- [x] `_documentTools.py` → UDM-Tools (`walkUdmBlocks`, `filterUdmByType`, `getUdmStructure`)
+- [x] E2E-Test: PDF → Extract (UDM) → Loop (pro Seite) → AI → Aggregate → Consolidate
+- [x] E2E-Test: PDF → Extract → Filter (nur Tabellen) → AI → kein Loop
+- [x] E2E-Test: ZIP → Extract (references) → Loop (Fan-out) → Vision + Text → Merge → Aggregate → Consolidate
+
+### Phase 8: Dokumentation & Abschluss
+
+> **Cursor-Empfehlung:** Composer (Fast) reicht. Reine Dokumentation und Verifikation.
+
+- [x] RBAC / Permissions: Keine Änderung nötig
+- [x] Neutralisierung: ContentBlock.raw mit sensiblen Daten → Bridge zu ContentPart
+- [x] Navigation / Routing: Keine Änderung
+- [x] Billing-Impact: Loop-Iterationen zählen als einzelne AI-Calls → bestehende Logik greift
+
+---
+
+## Akzeptanzkriterien
+
+| # | Kriterium (Given-When-Then) | Prio |
+|---|---------------------------|------|
+| 1 | Given ein PDF mit 5 Seiten, When `context.extractContent`-Node ausgeführt, Then UdmDocument mit 5 StructuralNodes (role=page) — **kein AI-Call**, nur Parsing | must |
+| 2 | Given der Extract-Node-Output (UDM), When mit `data.filter` auf `contentType=="table"` gefiltert, Then nur Tabellen-ContentBlocks im Output | must |
+| 3 | Given ein DOCX mit 3 Sections, When UDM-Extraktion, Then 3 StructuralNodes mit role=section und korrekten Labels | must |
+| 4 | Given ein UDM-Dokument, When `_udmToContentParts()`, Then identische ContentPart-Liste wie bei direkter Extraktion (Bridge) | must |
+| 5 | Given ein Workflow mit Loop über `document.children`, When ausgeführt, Then Body-Nodes pro StructuralNode einmal durchlaufen | must |
+| 6 | Given ForEach → AI → Aggregate → Consolidate, When 3 Seiten verarbeitet, Then Consolidate erhält 3 Ergebnisse und erzeugt Zusammenfassung | must |
+| 7 | Given Loop mit Fan-out zu 2 AI-Nodes + Merge, When pro Seite ausgeführt, Then Merge erhält beide AI-Ergebnisse pro Iteration | must |
+| 8 | Given `outputDetail=references` auf ZIP mit 1000 PDFs, When Extract ausgeführt, Then Referenzliste mit 1000 Einträgen, Memory < 100 MB | must |
+| 9 | Given Loop mit `concurrency: 5` über 100 Items, When ausgeführt, Then ~5x schneller als `concurrency: 1` | should |
+| 10 | Given Loop mit 10.000 Iterationen, When ausgeführt, Then weniger als 1000 AutoStepLog-Einträge (Batching) | should |
+| 11 | Given `context.extractContent` mit `outputDetail=structure`, When ausgeführt, Then nur Skelett ohne `raw`-Daten | should |
+| 12 | Given Loop-Workflow im Editor, When User den Graph betrachtet, Then Loop-Body-Nodes visuell als zusammengehörig erkennbar | should |
+| 13 | Given `data.consolidate` mit mode=table, When 100 CSV-Zeilen als Input, Then ein zusammengefügtes CSV als Output | should |
+| 14 | Given ZIP mit 10.000 PDFs, When E2E-Workflow (Extract refs → Loop → AI Vision Seite 1 → CSV), Then erfolgreich in < 24h (mit concurrency: 10) | nice |
+| 15 | Given ZIP mit 3 PDFs, When UDM-Extraktion, Then UdmArchive mit 3 UdmDocuments | nice |
+| 16 | Given ein Workflow mit `ai.prompt` und `data.filter` Nodes, When User den Graph betrachtet, Then AI-Badge nur auf `ai.prompt` sichtbar, nicht auf `data.filter` | must |
+| 17 | Given `data.consolidate` und `ai.consolidate` nebeneinander im Editor, When User vergleicht, Then nur `ai.consolidate` hat AI-Badge, `data.consolidate` nicht | must |
+
+---
+
+## Testplan
+
+*Stand beim Archivieren: Kerntests liegen in den unter „Abschlussbericht“ genannten Dateien. Die ursprünglich geplanten Einzelpfade (T2, T3, T6–T17) können bei Bedarf als separates Backlog ergänzt werden.*
+
+| ID | AC | Art | Automatisiert | Repo-Pfad | Status |
+|----|----|-----|--------------|-----------|--------|
+| T1 | 1 | unit | ja | gateway/tests/unit/datamodels/test_udm_models.py | done |
+| T2 | 1 | integration | ja | gateway/tests/integration/extraction/test_extract_udm_pipeline.py (statt urspr. `test_extract_node.py`) | done |
+| T3 | 2 | integration | ja | — (Backlog: dedizierter Filter-Flow) | backlog |
+| T4 | 3 | unit | ja | gateway/tests/unit/datamodels/test_udm_models.py | done |
+| T5 | 4 | unit | ja | gateway/tests/unit/datamodels/test_udm_bridge.py | done |
+| T6 | 1,3 | integration | ja | gateway/tests/integration/extraction/test_extract_udm_pipeline.py | done |
+| T7 | 5 | integration | ja | — (Backlog) | backlog |
+| T8 | 6 | integration | ja | gateway/tests/integration/workflows/test_execute_graph_loop_aggregate_consolidate.py | done |
+| T9 | 7 | integration | ja | — (Backlog) | backlog |
+| T10 | 8 | integration | ja | — (Backlog) | backlog |
+| T11 | 9 | integration | ja | — (Backlog) | backlog |
+| T12 | 10 | unit | ja | — (Backlog) | backlog |
+| T13 | 11 | unit | ja | — (Backlog) | backlog |
+| T14 | 12 | manual | nein | — | backlog |
+| T15 | 13 | integration | ja | — (Backlog) | backlog |
+| T16 | 14 | load | nein | — (manueller Load-Test) | backlog |
+| T17 | 15 | integration | ja | — (Backlog) | backlog |
+| T18 | 16 | unit | ja | gateway/tests/unit/nodeDefinitions/test_usesai_flag.py | done |
+| T19 | 17 | manual | nein | — (visueller Check im Editor) | backlog |
+
+---
+
+## Links
+
+- Idee: `c-work/0-ideas/unified-document-model.md`
+- Workflow-Engine Referenz: `b-reference/gateway/workflow.md`
+- AI-Agent Referenz: `b-reference/gateway/ai-agent.md`
+- Port-System Referenz: `c-work/4-done/2026-04-generic-graph-editor.md`
+
+---
+
+## Abschluss
+
+- [x] b-reference/ aktualisiert: `gateway/workflow.md` (neue Nodes, UDM-Loop-Pattern, Concurrency), `gateway/ai-agent.md` (neue UDM-Tools)
+- [x] TOPICS.md aktualisiert (neues Thema: UDM)
+- [x] Dieses Dokument → `wiki/z-archive/2026-04-unified-document-model.md` archiviert
diff --git a/z-archive/unified-document-model.md b/z-archive/unified-document-model.md
new file mode 100644
index 0000000..a4359c9
--- /dev/null
+++ b/z-archive/unified-document-model.md
@@ -0,0 +1,511 @@
+# Unified Document Model (UDM)
+
+## Konzept & Zielsetzung
+
+Das Unified Document Model definiert eine **generische, formatunabhängige Baumstruktur**, in die jeder Dokumenttyp (PDF, DOCX, PPTX, XLSX, HTML, ZIP) durch einen Extractor überführt wird. Dadurch können AI-Workflows, Nodes und Tools mit einem einzigen Objektmodell arbeiten – unabhängig vom Quellformat.
+
+### Designprinzipien
+
+- **3-Ebenen-Garantie**: Jedes Dokument hat exakt drei Verschachtelungsebenen (ausgenommen ZIP als Meta-Container).
+- **Einheitliche Blattknoten**: Alle atomaren Inhalte sind `ContentBlock`-Objekte mit identischer Attributstruktur.
+- **Generische Traversierung**: Workflow-Nodes (Loop, Filter, Transform, Map) arbeiten formatunabhängig über dieselbe Baumstruktur.
+- **Keine formatspezifischen Zwischenschichten**: Konzepte wie "Paragraph", "Row" oder "Cell" werden in den `ContentBlock` absorbiert, nicht als eigene Ebenen modelliert.
+
+---
+
+## Architekturübersicht
+
+```
+┌─────────────────────────────────────────────────────┐
+│ Level 1 — Document │
+│ ┌───────────────────────────────────────────────┐ │
+│ │ Level 2 — StructuralNode │ │
+│ │ ┌─────────────────────────────────────────┐ │ │
+│ │ │ Level 3 — ContentBlock (Blattknoten) │ │ │
+│ │ └─────────────────────────────────────────┘ │ │
+│ └───────────────────────────────────────────────┘ │
+└─────────────────────────────────────────────────────┘
+```
+
+### Ebenen im Detail
+
+| Ebene | Typ | Beschreibung |
+|-------|-----|-------------|
+| **Level 1** | `Document` | Wurzelknoten. Repräsentiert ein einzelnes Quelldokument. |
+| **Level 2** | `StructuralNode` | Strukturelle Gliederungseinheit: Seite, Abschnitt, Slide oder Sheet. |
+| **Level 3** | `ContentBlock` | Atomarer Inhalt: Text, Bild, Tabelle, Code, Medien, Link oder Formel. |
+
+---
+
+## Datenmodell
+
+### Document (Level 1)
+
+Der Wurzelknoten pro Quelldatei.
+
+```typescript
+interface Document {
+ id: string;
+ role: "document";
+ source_type: "pdf" | "docx" | "pptx" | "xlsx" | "html";
+ source_path: string;
+ metadata: Metadata;
+ children: StructuralNode[];
+}
+```
+
+| Feld | Typ | Beschreibung |
+|------|-----|-------------|
+| `id` | `string` | Eindeutige ID (UUID) |
+| `role` | `"document"` | Immer `"document"` auf Level 1 |
+| `source_type` | `string` | Originalformat der Quelldatei |
+| `source_path` | `string` | Pfad der Originaldatei (relativ zum Workspace oder Archiv) |
+| `metadata` | `Metadata` | Dokument-Metadaten |
+| `children` | `StructuralNode[]` | Liste der strukturellen Einheiten |
+
+---
+
+### StructuralNode (Level 2)
+
+Die Gliederungseinheit innerhalb eines Dokuments.
+
+```typescript
+interface StructuralNode {
+ id: string;
+ role: "page" | "section" | "slide" | "sheet";
+ index: number;
+ label: string | null;
+ metadata: Metadata;
+ children: ContentBlock[];
+}
+```
+
+| Feld | Typ | Beschreibung |
+|------|-----|-------------|
+| `id` | `string` | Eindeutige ID |
+| `role` | `string` | Art der Struktureinheit (formatabhängig, aber aus fester Menge) |
+| `index` | `number` | 0-basierte Position innerhalb des Dokuments |
+| `label` | `string?` | Optionaler Name (Sheet-Name, Abschnittsüberschrift, Slide-Titel) |
+| `metadata` | `Metadata` | Zusätzliche Informationen zur Struktureinheit |
+| `children` | `ContentBlock[]` | Liste der atomaren Inhalte |
+
+#### Rollen-Zuordnung pro Format
+
+| Quellformat | `role` | Entspricht im Original |
+|-------------|--------|----------------------|
+| PDF | `page` | Seite |
+| DOCX | `section` | Abschnitt (Heading-basierte Gliederung) |
+| PPTX | `slide` | Folie |
+| XLSX | `sheet` | Tabellenblatt |
+| HTML | `section` | Semantischer Bereich (``, ``, `