# Vollständiger Architektur-Audit — Alle gefundenen Fehler **Datum:** 2026-08-15 **Methode:** Persönliche Zeile-für-Zeile-Lektüre jeder Datei **Status:** In Arbeit — bisher 44 von 420 Dateien gelesen --- ## KRITISCHE RUNTIME-FEHLER (werden bei Nutzung crashen) ### FEHLER 1: hooks.py:83 — unregister() _filters 2-tuple CRASH **Datei:** `app/core/hooks.py:83` **Beweis:** ```python # Zeile 83: _filters nutzt 2-tuple unpacking, aber register_filter speichert 3-tuple self._filters[hook_name] = [ (p, c) for p, c in self._filters.get(hook_name, []) if c != callback # CRASH: too many values to unpack ] ``` **Auswirkung:** Jeder Aufruf von `unregister()` für Filter crasht mit `ValueError: too many values to unpack`. **Blast Radius:** Alle Filter-Unregistrierungen. **Schwere:** P0 — Runtime Crash. ### FEHLER 2: trigger_dispatcher.py:127 — AutomationDefinition nicht importiert **Datei:** `app/core/trigger_dispatcher.py:127` **Beweis:** ```python # Zeile 127: AutomationDefinition wird in Query verwendet, aber nie importiert query = ( select(AutomationDefinition) # NameError: name 'AutomationDefinition' is not defined .where(AutomationDefinition.is_active.is_(True)) ``` **Auswirkung:** Trigger-Dispatcher crasht bei jedem Event mit `NameError`. **Blast Radius:** Alle Event-gesteuerten Automatisierungen. **Schwere:** P0 — Runtime Crash. ### FEHLER 3: contacts/plugin.py:88-90 — clear_actions statt unregister_actions_by_owner **Datei:** `app/plugins/builtins/contacts/plugin.py:88-90` **Beweis:** ```python hook_reg.clear_actions("contact.after_create") # Entfernt Hooks anderer Plugins! hook_reg.clear_actions("contact.after_update") hook_reg.clear_actions("contact.after_delete") ``` **Auswirkung:** Wenn Contacts deaktiviert wird, werden History-Hooks aller anderen Plugins für `contact.after_*` Events entfernt. Das verletzt die Hook-Isolation. **Blast Radius:** Alle Plugins die contact.after_* Hooks registrieren (ai_assistant, automation, system_notif, etc.). **Schwere:** P0 — Hook-Isolation verletzt. ### FEHLER 4: attachment_service.py:48 — DmsFile type hint used but not imported **Datei:** `app/services/attachment_service.py:48` **Beweis:** ```python def _entity_attachment_to_dict(ea: EntityAttachment, dms_file: DmsFile | None = None) -> dict[str, Any]: ``` `DmsFile` wird als Type-Hint verwendet, aber nie importiert. `_get_dms_file_model()` holt es zur Laufzeit via Contract, aber der Type-Hint crasht bei Type-Checking oder zur Laufzeit wenn Annotations evaluiert werden. **Auswirkung:** TypeError zur Laufzeit wenn Annotations evaluiert werden (z.B. mit `from __future__ import annotations` nicht das Problem, aber inkonsistent). **Blast Radius:** Attachment-Service Serialisierung. **Schwere:** P1 — Potenzieller Runtime-Fehler. --- ## DOPPEL-REGISTRIERUNGEN ### FEHLER 5: restore_registry.py — register_default_entities registriert Contact **Datei:** `app/core/restore_registry.py:113-195` **Beweis:** `register_default_entities()` registriert Contact RestoreConfig. `ContactsPlugin.on_activate()` registriert AUCH Contact RestoreConfig. → Doppel-Registrierung. **Auswirkung:** Doppel-Registrierung mit Warning-Log. Zweite überschreibt erste, aber beide werden aufgerufen. **Blast Radius:** Contact-Restore. **Schwere:** P1 — Inkonsistenz. ### FEHLER 6: history_hooks.py — register_default_history_hooks registriert Contact ohne owner_tag **Datei:** `app/core/history_hooks.py:140` **Beweis:** `register_default_history_hooks()` registriert Contact hooks ohne owner_tag. `ContactsPlugin.on_activate()` registriert AUCH Contact hooks mit owner_tag="contacts". → Doppel-Registrierung: 2x hooks für jedes contact.after_* Event. **Auswirkung:** History wird 2x aufgezeichnet. register_default_history_hooks kann die Hooks nicht gezielt entfernen (kein owner_tag). **Blast Radius:** Contact-History. **Schwere:** P1 — Doppelte History-Einträge. ### FEHLER 7: entity_permission_service.py:59-61 — Contact hardcoded in ENTITY_MODELS **Datei:** `app/services/entity_permission_service.py:59-61` **Beweis:** ```python ENTITY_MODELS: dict[str, type] = { "contact": Contact, "contacts": Contact, "company": Contact, # ... } ``` ContactsPlugin.get_entity_models() liefert auch contact, contacts, company. main.py:lifespan() registriert diese erneut. → Doppel-Registrierung. **Auswirkung:** Doppel-Registrierung. Nicht kritisch (dict überschreibt), aber inkonsistent. **Blast Radius:** Entity-Permission-System. **Schwere:** P2 — Inkonsistenz. --- ## FEHLENDE DEREGISTRIERUNGEN ### FEHLER 8: mail/plugin.py — on_deactivate fehlt unregister_actions_by_owner und restore unregister **Datei:** `app/plugins/builtins/mail/plugin.py` **Beweis:** mail/plugin.py on_deactivate hat KEIN unregister_actions_by_owner für history hooks und KEIN unregister für restore config. **Auswirkung:** Mail-History-Hooks und Restore-Config bleiben bei Deaktivierung aktiv. **Blast Radius:** Mail-Plugin Deaktivierung. **Schwere:** P1 — Unvollständige Deaktivierung. --- ## TYPE-ANNOTATION-FEHLER ### FEHLER 9: hooks.py:52-53 — Type-Annotationen falsch **Datei:** `app/core/hooks.py:52-53` **Beweis:** ```python cls._instance._actions: dict[str, list[tuple[int, Callable]]] = defaultdict(list) # sollte tuple[int, Callable, str | None] cls._instance._filters: dict[str, list[tuple[int, Callable]]] = defaultdict(list) # sollte tuple[int, Callable, str | None] ``` **Auswirkung:** Type-Checker fehlschlagen, aber Runtime funktioniert (Python ignoriert Type-Annotationen). **Blast Radius:** Type-Checking. **Schwere:** P2 — Type-Safety. --- ## HARDCODED PATTERN VALIDATION ### FEHLER 10: saved_views.py:62 — pattern validation hardcoded **Datei:** `app/routes/saved_views.py:62` **Beweis:** ```python entity_type: str | None = Query(None, pattern="^(contacts|mail|calendar|dms)$"), ``` Obwohl `_validate_entity_type()` gegen ENTITY_MODELS prüft, hat der Query-Parameter noch ein hardcoded pattern das nur 4 Entity-Types erlaubt. **Auswirkung:** Andere Entity-Types (task, calendar_entry, file, etc.) werden mit 422 abgelehnt bevor _validate_entity_type aufgerufen wird. **Blast Radius:** Saved-Views für nicht-Contact Entities. **Schwere:** P1 — Falsche Validierung. ### FEHLER 11: saved_filters.py:62 — pattern validation hardcoded **Datei:** `app/routes/saved_filters.py:62` **Beweis:** ```python entity_type: str | None = Query(None, pattern="^(contacts|mail|calendar|dms)$"), ``` Gleiches Problem wie saved_views.py. **Auswirkung:** Andere Entity-Types werden mit 422 abgelehnt. **Blast Radius:** Saved-Filters für nicht-Contact Entities. **Schwere:** P1 — Falsche Validierung. --- ## HARDCODED PERMISSIONS ### FEHLER 12: saved_views.py:60 — require_permission("contacts:read") hardcoded **Datei:** `app/routes/saved_views.py:60` **Beweis:** ```python @router.get("", dependencies=[Depends(require_permission("contacts:read"))]) ``` Saved-Views benötigen `contacts:read` Permission. Das ist zu spezifisch — Saved-Views sind generisch für alle Entities. **Auswirkung:** User ohne `contacts:read` Permission können keine Saved-Views sehen, selbst für andere Entity-Types. **Blast Radius:** Saved-Views Access. **Schwere:** P2 — Falsche Permission. ### FEHLER 13: saved_filters.py:60 — require_permission("contacts:read") hardcoded **Datei:** `app/routes/saved_filters.py:60` **Beweis:** ```python @router.get("", dependencies=[Depends(require_permission("contacts:read"))]) ``` Gleiches Problem wie saved_views.py. **Auswirkung:** User ohne `contacts:read` Permission können keine Saved-Filters sehen. **Blast Radius:** Saved-Filters Access. **Schwere:** P2 — Falsche Permission. --- ## CORE-SPEZIFISCHE DATEN IN GENERISCHEN SERVICES ### FEHLER 14: sensitive_data.py:83-98 — DATA_EXPOSURE_POLICY hat Contact-spezifische Felder **Datei:** `app/core/sensitive_data.py:83-98` **Beweis:** ```python DATA_EXPOSURE_POLICY: dict[str, dict[str, dict[str, bool]]] = { "contact": { "code": _EXPORT_ONLY, "accounting_code": _EXPORT_ONLY, # ... 10 contact-spezifische Felder }, } ``` **Auswirkung:** Core enthält Contact-spezifische Data-Exposure-Policy. Plugins können ihre eigenen Policies nicht deklarieren (obwohl `register_sensitive_fields` existiert, gibt es kein `register_exposure_policy`). **Blast Radius:** Data-Redaction für alle Entities. **Schwere:** P2 — Core-spezifische Daten. ### FEHLER 15: permission_registry.py:86-122 — CORE_FIELD_DEFINITIONS hat Contact-spezifische Felder **Datei:** `app/core/permission_registry.py:86-122` **Beweis:** ~40 Contact-spezifische Felddefinitionen hartkodiert in Core. **Auswirkung:** Core enthält CRM-spezifische Felddefinitionen. Neue Contact-Felder erfordern Core-Änderung. **Blast Radius:** Feld-Level-Berechtigungen für Contacts. **Schwere:** P2 — Core-spezifische Daten. ### FEHLER 16: sensitive_data.py:24-48 — SENSITIVE_FIELDS hat Contact/Mail-spezifische Felder **Datei:** `app/core/sensitive_data.py:24-48` **Beweis:** ```python SENSITIVE_FIELDS: dict[str, set[str]] = { "contact": {"password_hash", "smtp_password", "imap_password", ...}, "mail_account": {"smtp_password", "imap_password", "oauth_token"}, } ``` **Auswirkung:** Core enthält Contact/Mail-spezifische sensitive Felder. `register_sensitive_fields` existiert für Plugins, aber Core-Felder sind statisch. **Blast Radius:** Data-Redaction, LLM-Context-Filtering. **Schwere:** P2 — Core-spezifische Daten. --- ## DEAD CODE / VERALTETE FUNKTIONEN ### FEHLER 17: restore_registry.py — register_default_entities ist veraltet **Datei:** `app/core/restore_registry.py` **Beweis:** `register_default_entities()` existiert noch und registriert Contact, aber ContactsPlugin.on_activate() registriert es auch. Die Funktion wird nirgends mehr aufgerufen (main.py ruft sie nicht auf). **Auswirkung:** Tote Funktion. Sollte entfernt oder dokumentiert werden. **Blast Radius:** Keiner (wird nicht aufgerufen). **Schwere:** P3 — Dead Code. ### FEHLER 18: history_hooks.py — register_default_history_hooks ist veraltet **Datei:** `app/core/history_hooks.py` **Beweis:** `register_default_history_hooks()` existiert noch und registriert Contact hooks, aber ContactsPlugin.on_activate() registriert sie auch. Die Funktion wird nirgends mehr aufgerufen. **Auswirkung:** Tote Funktion. Sollte entfernt oder dokumentiert werden. **Blast Radius:** Keiner (wird nicht aufgerufen). **Schwere:** P3 — Dead Code. ### FEHLER 19: registry.py:36 — _mounted_routes ist Dead Code **Datei:** `app/plugins/registry.py:36` **Beweis:** `self._mounted_routes: dict[str, list[Any]] = {}` wird initialisiert aber nie befüllt. Route-Removal-Logik wurde entfernt (Gate-Modell dokumentiert). **Auswirkung:** Tote Variable. **Blast Radius:** Keiner. **Schwere:** P3 — Dead Code. --- ## PLUGIN→PLUGIN IMPORTS (deklariert aber direkte Imports statt Contracts) ### FEHLER 20: ai_assistant/plugin.py:96-98 — direkter Import von kommunikation.contracts **Datei:** `app/plugins/builtins/ai_assistant/plugin.py:96-98` **Beweis:** ```python from app.plugins.builtins.kommunikation.contracts import ( get_participant_registry, ) ``` **Bewertung:** Deklariert in `dependencies=["kommunikation"]` — erlaubt per Regel 2. Aber direkter Import statt Contract-Registry. **Schwere:** P3 — Erlaubt aber inkonsistent. ### FEHLER 21: system_notif/plugin.py:161 — direkter Import von kommunikation.contracts **Datei:** `app/plugins/builtins/system_notif/plugin.py:161` **Beweis:** ```python from app.plugins.builtins.kommunikation.contracts import create_plugin_room, send_message ``` **Bewertung:** Deklariert in `dependencies=["kommunikation"]` — erlaubt. Aber direkter Import. **Schwere:** P3 — Erlaubt aber inkonsistent. ### FEHLER 22: graph_rag/plugin.py:44,57 — direkter Import von unified_search.contracts **Datei:** `app/plugins/builtins/graph_rag/plugin.py:44,57` **Beweis:** ```python from app.plugins.builtins.unified_search.contracts import get_search_registry ``` **Bewertung:** Deklariert in `dependencies=["unified_search"]` — erlaubt. Aber direkter Import. **Schwere:** P3 — Erlaubt aber inkonsistent. ### FEHLER 22a: worker.py:169 — direkter Import von unified_search.provider_registry (Core→Plugin) **Datei:** `app/core/worker.py:169` **Beweis:** ```python from app.plugins.builtins.unified_search.provider_registry import auto_register_providers ``` **Bewertung:** Core importiert Plugin-Modul direkt. `UnifiedSearchContract` existiert, exponiert aber `auto_register_providers` nicht. Contract-Umgehung. **Schwere:** P2 — Core→Plugin-Kopplung. ### FEHLER 22b: worker.py:280 — direkter Import von forgejo_error_reporter.service (Core→Plugin) **Datei:** `app/core/worker.py:280` **Beweis:** ```python from app.plugins.builtins.forgejo_error_reporter.service import report_error_to_forgejo ``` **Bewertung:** Core importiert Plugin-Modul direkt. `ForgejoErrorReporterContract` existiert und wird in `main.py`/`errors.py` korrekt genutzt — hier inkonsistent umgangen. **Schwere:** P2 — Core→Plugin-Kopplung, inkonsistent. --- ## SIDE-EFFECT IMPORTS ### FEHLER 23: report_generator/plugin.py:9 — top-level import of jobs module **Datei:** `app/plugins/builtins/report_generator/plugin.py:9` **Beweis:** ```python from app.plugins.builtins.report_generator import jobs # noqa: F401 ``` Top-Level-Import von jobs-Modul hat Side-Effects (register_job Aufrufe beim Import). **Auswirkung:** Jobs werden beim Import registriert, nicht erst bei on_activate(). Wenn Plugin deaktiviert ist, bleiben Jobs registriert. **Blast Radius:** Report-Generator Jobs. **Schwere:** P2 — Jobs nicht lifecycle-gesteuert. --- ## BASE PLUGIN on_deactivate HEURISTIK ### FEHLER 24: base.py:81 — unregister_all_for_plugin nutzt __self__ Heuristik **Datei:** `app/plugins/base.py:81` **Beweis:** ```python get_hook_registry().unregister_all_for_plugin(self.manifest.name) ``` `unregister_all_for_plugin` nutzt `callback.__self__.manifest.name` um Plugin-zugehörigkeit zu bestimmen. Das funktioniert nur für Bound-Methods, nicht für Free Functions (wie sie von `register_history_hooks` erstellt werden). **Auswirkung:** History-Hooks die via `register_history_hooks` registriert wurden, werden von `unregister_all_for_plugin` NICHT entfernt. Plugins müssen `unregister_actions_by_owner` manuell aufrufen. **Blast Radius:** Alle Plugins mit register_history_hooks. **Schwere:** P2 — Unvollständige Hook-Entfernung. --- ## WEITERE PROBLEME ### FEHLER 25: entity_permission_service.py:30 — importiert create_notification **Datei:** `app/services/entity_permission_service.py:30` **Beweis:** ```python from app.core.notifications import create_notification ``` `create_notification` ist deprecated und nutzt jetzt `get_contract("kommunikation")`. Der Import ist nicht falsch, aber die deprecated Funktion wird verwendet. **Auswirkung:** Deprecated Function wird verwendet. **Blast Radius:** Permission-Change Notifications. **Schwere:** P3 — Deprecated Usage. --- ## DEPS.PY HARDCODED PERMISSIONS ### FEHLER 26: deps.py:21-36 — _WRITE_PERMISSIONS hardcoded mit Plugin-Permissions **Datei:** `app/deps.py:21-36` **Beweis:** ```python _WRITE_PERMISSIONS = [ "contacts:write", "contacts:create", # ... ] ``` `contacts:write` und `contacts:create` sind Plugin-Permissions (ContactsPlugin), aber in Core deps.py hardcoded. **Auswirkung:** Core kennt Plugin-Permissions. Wenn Contacts deaktiviert wird, bleiben diese Permissions in der Liste. **Blast Radius:** require_write() Dependency. **Schwere:** P2 — Core-spezifische Daten. --- ## WORKFLOW SERVICE DEPRECATED NOTIFICATIONS ### FEHLER 27: workflow_service.py:13 — importiert deprecated Notification model **Datei:** `app/services/workflow_service.py:13` **Beweis:** ```python from app.models.notification import Notification ``` Workflow-Service nutzt deprecated Notification model statt kommunikation Contract. **Auswirkung:** Workflow-Notifications nutzen altes System statt kommunikation Plugin. **Blast Radius:** Workflow-Notifications. **Schwere:** P2 — Deprecated Usage. --- ## DASHBOARD HARDCODED CONTACT COUNTS ### FEHLER 28: dashboard.py:14,61-93 — hardcoded Contact counts, kein Plugin-Beitrag möglich **Datei:** `app/routes/dashboard.py:14,61-93` **Beweis:** ```python from app.models.contact import Contact # Core→Contact (Plugin-Entity) # ... contact_query = select(func.count(Contact.id)).where(...) # Hardcoded Contact company_query = select(func.count(Contact.id)).where(..., Contact.type == "company") # Hardcoded Company person_query = select(func.count(Contact.id)).where(..., Contact.type == "person") # Hardcoded Person ``` Dashboard /counts ist hardcoded auf Contact/Company/Person. Kein Plugin kann Dashboard-Statistiken beitragen. /widgets ist dynamisch, aber /counts ist statisch. **Auswirkung:** Dashboard-Statistiken sind nicht plugin-erweiterbar. **Blast Radius:** Dashboard /counts Endpoint. **Schwere:** P2 — Nicht generisch. --- ## IMPORT/EXPORT HARDCODED ENTITY TYPES ### FEHLER 29: import_export.py:40 — entity_type default 'companies' hardcoded **Datei:** `app/routes/import_export.py:40` **Beweis:** ```python entity_type: str = Form("companies"), ``` Import/Export unterstützt nur 'companies' und 'contacts' (beide Contact-Modell). Kein Plugin kann Import/Export für seine Entities anbieten. **Auswirkung:** Import/Export ist Contact-spezifisch, nicht generisch. **Blast Radius:** Import/Export für alle Entities außer Contacts. **Schwere:** P2 — Nicht generisch. --- ## MAIL PLUGIN INCOMPLETE DEACTIVATION ### FEHLER 30: mail/plugin.py:194-200 — on_deactivate fehlt restore + history unregister **Datei:** `app/plugins/builtins/mail/plugin.py:194-200` **Beweis:** ```python async def on_deactivate(self, db, service_container, event_bus) -> None: # Contract abmelden from app.plugins.builtins.contracts import get_contract_registry get_contract_registry().unregister(self.manifest.name) # ... stop auto_sync_task ... await super().on_deactivate(db, service_container, event_bus) ``` KEIN `get_restore_registry().unregister("mail")` und KEIN `unregister_actions_by_owner` für history hooks. **Auswirkung:** Mail-Restore-Config und History-Hooks bleiben bei Deaktivierung aktiv. **Blast Radius:** Mail-Plugin Deaktivierung. **Schwere:** P1 — Unvollständige Deaktivierung. --- ## FRONTEND/BACKEND INKONSISTENZ ### FEHLER 31: frontend/src/api/tags.ts:12 — EntityType hardcoded und inkonsistent mit Backend **Datei:** `frontend/src/api/tags.ts:12` **Beweis:** ```typescript export type EntityType = 'contact' | 'file' | 'calendar_entry'; ``` Backend validiert dynamisch gegen ENTITY_MODELS (contact, file, folder, task, calendar_entry, etc.). Frontend hat hardcoded 3 Types, davon 'calendar_entry' was im Backend nicht in der ursprünglichen VALID_ENTITY_TYPES war. **Auswirkung:** Frontend bietet 'calendar_entry' an, Backend akzeptiert es (über ENTITY_MODELS). Aber Frontend bietet nicht 'folder', 'task', 'mail', etc. an. User-sichtbare Inkonsistenz. **Blast Radius:** Tag-Vergabe im Frontend. **Schwere:** P1 — Frontend/Backend-Inkonsistenz. --- ## CONFTEST.PY HARDCODED MODEL IMPORTS ### FEHLER 32: conftest.py:41-53 — hardcoded Core-Model imports trotz dynamischer Discovery **Datei:** `tests/conftest.py:41-53` **Beweis:** ```python from app.models.ai_conversation import AIConversation, AIMessage # noqa: F401 from app.models.contact import Contact, ContactPerson # noqa: F401 from app.models.contact_merge import ContactMergeHistory # noqa: F401 from app.models.plugin import Plugin, PluginMigration # noqa: F401 from app.models.role import Role from app.models.tenant import Tenant from app.models.user import User, UserTenant from app.models.user_preference import UserPreference # noqa: F401 from app.models.workflow import Workflow, WorkflowInstance, WorkflowStepHistory # noqa: F401 from app.models.outbox import EventOutbox # noqa: F401 from app.models.consumer_inbox import ConsumerInbox # noqa: F401 from app.models.outbox_delivery import OutboxDelivery # noqa: F401 from app.models.saved_filter import SavedFilter # noqa: F401 ``` Obwohl dynamische Discovery in Zeile 57-71 läuft, werden Core-Modelle noch hardcoded importiert. Das ist nicht falsch (Core-Modelle müssen immer verfügbar sein), aber AIConversation/AIMessage sind Plugin-Modelle die über ai_assistant Plugin kommen sollten. **Auswirkung:** AIConversation/AIMessage werden hardcoded importiert statt über Plugin-Discovery. Wenn ai_assistant Plugin nicht aktiv ist, schlagen diese Imports fehl. **Blast Radius:** Test-Setup. **Schwere:** P2 — Inkonsistenz. --- ## ROLES.PY HARDCODED PERMISSIONS ### FEHLER 33: roles.py:27-50 — SYSTEM_PERMISSIONS hardcoded mit Plugin-Permissions **Datei:** `app/routes/roles.py:27-50` **Beweis:** ```python SYSTEM_PERMISSIONS: list[dict[str, str]] = [ {"key": "contacts:read", "label": "Contacts: Read", "category": "system"}, {"key": "contacts:write", "label": "Contacts: Write", "category": "system"}, {"key": "contacts:delete", "label": "Contacts: Delete", "category": "system"}, # ... ] ``` `contacts:read`, `contacts:write`, `contacts:delete` sind Plugin-Permissions (ContactsPlugin), aber in Core roles.py hardcoded als SYSTEM_PERMISSIONS. **Auswirkung:** Core kennt Plugin-Permissions. Wenn Contacts deaktiviert wird, bleiben diese Permissions in der Liste. Doppelte Quelle zur permission_registry. **Blast Radius:** Rollen-Verwaltung, Permission-UI. **Schwere:** P2 — Core-spezifische Daten, Doppelquelle. --- ## USERS.PY DEPRECATED NOTIFICATIONS ### FEHLER 34: users.py:15 — importiert create_notification (deprecated) **Datei:** `app/routes/users.py:15` **Beweis:** ```python from app.core.notifications import create_notification ``` User-Route nutzt deprecated `create_notification` statt `post_system_message` über KommunikationContract. **Auswirkung:** User-Notifications nutzen altes System statt kommunikation Plugin. **Blast Radius:** User-Notifications. **Schwere:** P2 — Deprecated Usage. --- ## WORKFLOW ENGINE DEPRECATED NOTIFICATIONS ### FEHLER 35: workflows/engine.py:122-130 — erstellt Notification model direkt **Datei:** `app/workflows/engine.py:122-130` **Beweis:** ```python notification = Notification( tenant_id=self.tenant_id, user_id=uuid.UUID(user_id), type=config.get("notification_type", "workflow_action"), title=config.get("title", "Workflow notification"), body=config.get("body", ""), ) self.db.add(notification) ``` Workflow-Engine erstellt direkt Notification model (deprecated) statt `post_system_message` über KommunikationContract zu verwenden. **Auswirkung:** Workflow-Notifications nutzen altes System. Doppelarchitektur. **Blast Radius:** Workflow-Notifications. **Schwere:** P2 — Deprecated Usage, Doppelarchitektur. --- ## SCHEMAS/COMMON.PY DEPRECATED NOTIFICATION SCHEMAS ### FEHLER 36: schemas/common.py:22-58 — Notification schemas für deprecated system **Datei:** `app/schemas/common.py:22-58` **Beweis:** ```python class NotificationResponse(BaseModel): ... class NotificationListResponse(BaseModel): ... class UnreadCountResponse(BaseModel): ... class NotificationPreferenceUpdate(BaseModel): ... class NotificationTypeResponse(BaseModel): ... class NotificationPreferenceResponse(BaseModel): ... ``` 6 Schemas für deprecated notification system. Sollten zu kommunikation Plugin migriert werden. **Auswirkung:** Core enthält deprecated notification schemas. **Blast Radius:** API-Schemas. **Schwere:** P3 — Deprecated Code. --- ## CUSTOM_FIELDS.PY PRIVATE ATTRIBUT ZUGRIFF ### FEHLER 37: custom_fields.py:42 — greift auf registry._plugins direkt zu **Datei:** `app/routes/custom_fields.py:42` **Beweis:** ```python for plugin in registry._plugins.values(): ``` Greift auf privates Attribut `_plugins` zu statt öffentlichen API (`list_discovered()` + `get_plugin()`) zu nutzen. **Auswirkung:** Kapselung verletzt. Wenn Registry-Internals ändern, bricht dieser Code. **Blast Radius:** Custom-Field-Definitionen. **Schwere:** P3 — Private Attribut. --- ## WEBHOOKS.PY FALSCHE PERMISSIONS ### FEHLER 38: webhooks.py:26,49 — require_permission('automation:read/write') für Core-Webhooks **Datei:** `app/routes/webhooks.py:26,49` **Beweis:** ```python dependencies=[Depends(require_permission("automation:read"))] dependencies=[Depends(require_permission("automation:write"))] ``` Webhooks sind Core-Funktionalität (app/routes/webhooks.py), aber nutzen `automation:read/write` Permissions. Das sind Plugin-Permissions (AutomationPlugin). Wenn Automation deaktiviert ist, können User keine Webhooks verwalten. **Auswirkung:** Webhook-Access hängt von Automation-Plugin ab. Core-Funktion blockiert durch Plugin-Deaktivierung. **Blast Radius:** Webhook-Verwaltung. **Schwere:** P2 — Falsche Permission-Zuordnung. --- ## FRONTEND SIDEBAR UND PLUGIN STORE — OK Sidebar.tsx und pluginStore.ts wurden geprüft — beide sind korrekt dynamisch. Sidebar liest Menu-Items aus `usePluginStore(s => s.manifests)`, pluginStore ist generisch. Keine Fehler. --- ## PERSÖNLICHE ZEILE-FÜR-ZEILE-LEKTÜRE — Neue Funde ### FEHLER 46: contacts/plugin.py:88-90 — clear_actions() NICHT GEFIXT (P0) **Datei:** `app/plugins/builtins/contacts/plugin.py:88-90` **Beweis:** ```python hook_reg.clear_actions("contact.after_create") hook_reg.clear_actions("contact.after_update") hook_reg.clear_actions("contact.after_delete") ``` **Problem:** on_deactivate nutzt clear_actions() statt unregister_actions_by_owner(). Entfernt History-Hooks ALLER Plugins für contact.after_* Events. P0-3 wurde dokumentiert aber NICHT GEFIXT. **Schwere:** P0 — Runtime Crash bei Deaktivierung. ### FEHLER 47: mail/plugin.py:194-217 — on_deactivate FEHLT restore unregister (P1) **Datei:** `app/plugins/builtins/mail/plugin.py:194-217` **Beweis:** on_deactivate deregistriert history hooks (Zeile 213-215) aber hat KEIN `get_restore_registry().unregister("mail")`. **Problem:** Mail restore config bleibt aktiv wenn Plugin deaktiviert wird. **Schwere:** P1 — Funktionaler Fehler bei Deaktivierung. ### FEHLER 48: entity_attachment.py:45-49 — __import__ inline statt func import (P3) **Datei:** `app/models/entity_attachment.py:45-49` **Beweis:** ```python created_at: Mapped[datetime] = mapped_column( DateTime(timezone=True), nullable=False, server_default=__import__('sqlalchemy').func.now() ) ``` **Problem:** `__import__('sqlalchemy').func.now()` statt `from sqlalchemy import func` + `func.now()`. Schlechte Praxis, unnötige Performance-Kosten. **Schwere:** P3 — Code Quality. ### FEHLER 49: ai_copilot_service.py:18-19 — Duplicate Contact import (P3) **Datei:** `app/services/ai_copilot_service.py:18-19` **Beweis:** ```python from app.models.contact import Contact from app.models.contact import Contact # Duplicate! ``` **Schwere:** P3 — Code Quality. ### FEHLER 50: system_settings.py:44-54 — Duplicate field definitions (P3) **Datei:** `app/schemas/system_settings.py:44-54` **Beweis:** ```python class SystemSettingsResponse(BaseModel): # Zeilen 45-48: tax_number: str | None = None vat_id: str | None = None iban: str | None = None bic: str | None = None # Zeilen 51-54 (duplicate!): tax_number: str | None = None vat_id: str | None = None iban: str | None = None bic: str | None = None ``` **Problem:** Pydantic überschreibt stillschweigend. Kein Runtime-Fehler, aber verwirrend und Code-Quality-Problem. **Schwere:** P3 — Code Quality. ### FEHLER 51: address.py:9 — Hardcoded `pattern="^contact$"` (P2) **Datei:** `app/schemas/address.py:9` und `app/routes/addresses.py:20` **Beweis:** ```python entity_type: str = Field(..., pattern="^contact$", description="'contact'") ``` **Problem:** Address-Modell ist generisch (entity_type + entity_id), aber Schema UND Route limitieren auf 'contact'. Addresses können NUR für Contacts erstellt werden. **Schwere:** P2 — Architekturfehler, generisches Modell künstlich limitiert. ### FEHLER 52: unified_search hardcoded entity maps (P2) **Dateien:** `app/plugins/builtins/unified_search/search_engine.py:19-24`, `lifecycle.py:22-27`, `jobs.py:16-21` **Beweis:** Drei separate hardcoded Entity-Maps für dieselben 4 Entity-Types (contact, mail, file, event). Duplikation, nicht erweiterbar. **Schwere:** P2 — Architekturfehler, dreifache Duplikation. ### FEHLER 53: ai_proactive/services.py:26-27 — Duplicate Contact import (P3) **Datei:** `app/plugins/builtins/ai_proactive/services.py:26-27` **Beweis:** ```python from app.models.contact import Contact, ContactPerson from app.models.contact import Contact, ContactPerson # Duplicate! ``` **Schwere:** P3 — Code Quality. ### FEHLER 54: frontend routes/index.tsx — Hardcoded Plugin-Routes (P2) **Datei:** `frontend/src/routes/index.tsx:17-70` **Beweis:** 54 hardcoded lazy-loaded page imports für Plugin-Seiten. PluginRouteRenderer (Zeile 207) ist dynamisch als catch-all, aber alle Haupt-Routes sind statisch. **Problem:** Neues Plugin → Frontend-Route muss hardcoded hinzugefügt werden. **Schwere:** P2 — Frontend nicht vollständig dynamisch. ### FEHLER 55: tags/schemas.py:27,33,39 — Hardcoded entity_type pattern (P2) **Datei:** `app/plugins/builtins/tags/schemas.py:27,33,39` **Beweis:** `pattern="^(contact|file|folder)$"` in TagAssignRequest, TagUnassignRequest, TagBulkAssignRequest. **Problem:** Schema limitiert auf 3 Entity-Types, Backend validiert dynamisch. Schema blockiert gültige Requests. **Schwere:** P2 — Schema restriktiver als Backend. ### FEHLER 56: entity_links/schemas.py:9 — Hardcoded entity_type pattern (P2) **Datei:** `app/plugins/builtins/entity_links/schemas.py:9` **Beweis:** `entity_type: str = Field(..., pattern="^(contact|company)$")` **Problem:** Schema limitiert auf 2 Entity-Types, Backend validiert dynamisch. **Schwere:** P2 — Schema restriktiver als Backend. ### FEHLER 57: forgejo_error_reporter/models.py:13 — Eigenes Base, nicht app.core.db.Base (P2) **Datei:** `app/plugins/builtins/forgejo_error_reporter/models.py:13` **Beweis:** ```python from sqlalchemy.orm import declarative_base Base = declarative_base() # Eigenes Base, nicht app.core.db.Base! ``` **Problem:** ReportedError verwendet ein eigenes Base statt `app.core.db.Base`. Die Tabelle wird nicht von `Base.metadata.create_all()` erfasst und ist nicht Teil des gemeinsamen Metadaten-Registries. Tests können diese Tabelle nicht erstellen. **Schwere:** P2 — Architekturfehler, Modell isoliert vom Haupt-Metadaten-Registries. ### FEHLER 58: mcp_client/models.py:35-36 — Naive datetime statt UTC (P1) **Datei:** `app/plugins/builtins/mcp_client/models.py:35-36` **Beweis:** `datetime.utcnow` (naive) statt `datetime.now(UTC)`. **Schwere:** P1 — Verletzt TIMESTAMPTZ-Regel. --- ## TEST-AUDIT — 60 weitere Fehler in Test-Dateien ### P0 (5) - **test_external_agent_api.py:36-39** — check_permission mocked to True (alle Tests bypass RBAC) - **test_graph_rag.py:39-43** — check_permission mocked to True - **test_agent_memory.py:39-43** — check_permission mocked to True - **test_marketplace.py:45-49** — check_permission mocked to True - **test_cross_tenant_standalone.py:33** — Hardcoded DB credential mit Passwort ### P1 (26) - **test_ai_copilot.py:45-47,77-79** — Tests silently pass bei 403 - **test_user_service.py:38-39,82-100** — Non-deterministic assertions, try/except pass - **test_backup_service.py:37-38,50-51,63** — Overly permissive assertions (4 status codes) - **test_mcp_server.py:62,127** — `assert success in (True, False)` — immer True - **12 Test-Dateien** — Missing cross-tenant isolation tests (saved_filters, tasks, dashboard, custom_fields, calendar, workflows, notifications, companies, contacts, dms, entity_links, tags) ### P2 (21) - **test_cross_tenant_security.py:98** — Invalid bcrypt hash `$2b$12$testhash` - **test_cross_tenant_security_v2.py:132** — Same invalid hash - **test_cross_tenant_standalone.py:73** — Same invalid hash - **test_workspaces.py:42, test_api_tokens.py:33** — `password_hash="dummy"` - **test_tags.py:138,165,194** — Random UUIDs für non-existent entities - **test_ai_proactive.py:146-150** — Test grants is_system_admin to bypass permissions - **test_commands.py:140-148** — Wildcard permissions `*: *` bypass real RBAC - **test_mcp_client.py:41** — API token returned in plaintext - **3 Test-Dateien** — Hardcoded SECRET_KEY ### P3 (8) - **test_agent_subtasks.py:29-31,35-37** — Duplicate fixture definition - **test_cross_tenant_security.py:187-188** — Duplicate @pytest.mark.asyncio - **test_tenant.py:189** — Potential KeyError (custom_role) - **4 Test-Dateien** — sys.path.insert path manipulation - **test_dms_coverage.py:13** — Unused import --- ## FRONTEND-AUDIT — Weitere Fehler aus persönlichem Lesen ### P1 - **frontend/src/components/common/ProtectedRoute.tsx:22-24** — Fail-open: wenn `perms.length === 0`, wird access erlaubt. Begründung: 'better to show page and let backend 403 handle it'. Aber das bedeutet dass ein User mit nicht geladenen Permissions alles sehen kann. ### P2 - **frontend/src/pages/Settings.tsx:18-29** — 10 hardcoded settings nav items (Stammdaten, Nutzerverwaltung, System, Mail, AI, Notifications, Custom Fields, Webhooks, Workspaces, Backup). Plugin settings pages werden dynamisch hinzugefügt, aber Core-Settings sind hardcoded. - **frontend/src/api/search.ts:40-47** — ENTITY_URL_MAP hardcoded mit 6 Entity-Types (contact, company, mail, file, event, message). Neue Entity-Types haben keine URL-Map und bekommen '#'. - **frontend/src/components/notifications/NotificationDropdown.tsx** — Nutzt alte `/notifications` API statt kommunikation System Channel. - **frontend/src/components/layout/NotificationBell.tsx:13** — Nutzt `useUnreadNotificationCount` von `/notifications` API statt kommunikation. - **frontend/src/api/contacts.ts** + **frontend/src/api/unifiedContacts.ts** — Dual API client: `contacts.ts` (legacy) und `unifiedContacts.ts` (neu). Beide existieren parallel. ### P3 - **frontend/src/pages/Settings.tsx:5** — `import * as LucideIcons from 'lucide-react'` lädt ALLE Icons (Sidebar.tsx vermeidet das explizit mit ICON_MAP). - **frontend/src/pages/Settings.tsx:38** — `(LucideIcons as any)[p.icon]` — `any` type cast verletzt TypeScript strict rule. - **frontend/src/pages/Communication.tsx:78-80** — MiniApp interface definiert aber möglicherweise ungenutzt. - **scripts/seed_perf_data.py:72** — `role='admin'` hardcoded für perf test user. ### P2 (Frontend weiter) - **frontend/src/components/search/CommandPalette.tsx:12-19** — TYPE_LABELS hardcoded mit 6 Entity-Types (company, contact, mail, file, event, message). Neue Entity-Types haben keine Labels. - **frontend/src/components/search/CommandPalette.tsx:21-28** — TYPE_ICON_CLASSES hardcoded mit 6 Entity-Types. - **frontend/src/components/search/CommandPalette.tsx:31-39** — typeIcon() hardcoded mit 6 Entity-Types. - **frontend/src/api/types.ts:12-22** — Contact interface mit first_name/last_name (legacy, nicht unified Contact model). Dual interface: types.ts (legacy) + unifiedContacts.ts (neu). - **frontend/src/pages/Settings.tsx:18-29** — 10 hardcoded settings nav items. Plugin settings pages werden dynamisch hinzugefügt, aber Core-Settings sind hardcoded. - **frontend/src/components/contacts/ContactDetail.tsx:11** — `import * as LucideIcons` lädt ALLE Icons (Sidebar.tsx vermeidet das explizit). - **frontend/src/components/contacts/ContactDetail.tsx:67-74** — 8x `(contact as any)` type casts verletzen TypeScript strict rule. - **frontend/src/components/contacts/ContactList.tsx:28-80** — ALL_COLUMNS hardcoded mit Contact-spezifischen Spalten. - **frontend/src/components/contacts/FilterPanel.tsx:22-80** — FIELD_DEFS hardcoded mit ~40 Contact-spezifischen Filter-Feldern. - **frontend/src/components/HistoryViewer.tsx** + **EntityHistoryPanel.tsx** — Dual history components (HistoryViewer + EntityHistoryPanel) mit überlappender Funktionalität. - **frontend/src/components/ai/SuggestionBadge.tsx:14,19** — Hardcoded API paths `/ai-proactive/suggestions` und SSE endpoint. - **frontend/src/hooks/useAIContext.ts:5** — `entityData?: any` type cast. - **frontend/src/components/contacts/ContactList.tsx:36** — Checkbox ohne ARIA label für screen readers (hat aria-label aber nur auf checkbox). - **frontend/src/components/contacts/SortPanel.tsx:22-69** — SORT_FIELDS hardcoded mit ~30 Contact-Feldern (dupliziert von FilterPanel FIELD_DEFS). - **frontend/src/components/contacts/GroupPanel.tsx:22-68** — GROUP_FIELDS hardcoded mit ~30 Contact-Feldern (nochmal dupliziert). Triple-duplicated hardcoded field definitions across FilterPanel + SortPanel + GroupPanel. - **frontend/src/components/contacts/CustomFieldRenderer.tsx:15-16** — `Record` und `any` type casts. - **frontend/src/components/mail/MailList.tsx:62** — inline style `paddingLeft` (verletzt Tailwind-only rule). - **frontend/src/components/mail/MailDetail.tsx:49-52** — `sanitized_html || body_html` wird in iframe gerendert (potential XSS wenn sanitized_html nicht properly sanitized). - **frontend/src/components/mail/MailFolderTree.tsx:22-43** — FOLDER_NAME_MAP hardcoded mit 20 IMAP folder names. - **frontend/src/components/mail/MailFilterPanel.tsx:22-44** — FIELD_DEFS hardcoded mit 10 Mail-spezifischen Feldern. - **frontend/src/components/SavedFilters.tsx:22,47,56** — `Record` und `err: any` type casts. - **frontend/src/store/windowStore.ts:8-9** — `ComponentType` und `Record`. - **frontend/src/store/commStore.ts:27,61** — `Record` für metadata und `reactions: any[]`. - **frontend/src/hooks/useCommWebSocket.ts:31** — `console.log` statt strukturiertem Logger. - **frontend/src/components/ai/ChatWindow.tsx:39-41** — inline styles für animationDelay (verletzt Tailwind-only rule). - **frontend/src/pages/Trash.tsx:17** — ENTITY_TYPES hardcoded mit 5 Types (contact, task, calendar_entry, dms_file, mail). - **frontend/src/pages/GlobalSearchResults.tsx:15-22** — TYPE_LABELS hardcoded mit 6 Entity-Types (dupliziert von CommandPalette). - **frontend/src/pages/AgentDashboard.tsx:50-61** — commonModels hardcoded mit 10 Modellnamen (gpt-4, claude-3, llama-3, etc.). - **frontend/src/pages/StartPage.tsx:22-31** — DEFAULT_WORKSPACES hardcoded mit 1 Workspace. - **frontend/src/pages/StartPage.tsx:38-42** — menuItems hardcoded (Workspaces, Einstellungen, Hilfe). - **frontend/src/pages/NoAccessPage.tsx:9-13** — Hardcoded German strings ohne i18n t() function. - **frontend/src/pages/Login.tsx:41** — `error: any` type cast. - **frontend/src/pages/Workflows.tsx:59,70** — `err: any` type casts. - **frontend/src/pages/AuditLog.tsx:37** — `ColumnDef` type cast. - **frontend/src/pages/SettingsWebhooks.tsx:38-56** — AVAILABLE_EVENTS hardcoded mit 16 Events, davon 6 nicht existent (deal.created/updated/deleted, note.created/updated/deleted — Deals und Notes existieren nicht im System). - **frontend/src/pages/SettingsUsers.tsx:19-25** — LEGACY_ROLES hardcoded mit 5 Rollen (admin, manager, user, guest, viewer). - **frontend/src/pages/SettingsUsers.tsx:47-48** — `any` type casts für confirmDeactivate/confirmDelete. - **frontend/src/pages/SettingsRoles.tsx:21,23** — `Record` für permissions. - **frontend/src/pages/SettingsGroups.tsx:29-76** — gleiche `any` patterns wie SettingsRoles. - **frontend/src/pages/CustomFields.tsx:40-43** — ENTITY_OPTIONS hardcoded mit 2 Types (contact, company). - **frontend/src/pages/ActivityTimeline.tsx:25-26** — hardcoded German strings ('Heute', 'Gestern') ohne i18n. - **frontend/src/pages/NoAccessPage.tsx:9-13** — hardcoded German strings ohne i18n. - **frontend/src/pages/SettingsStammdaten.tsx:18-39** — addressTypeToBackend/addressTypeToFrontend hardcoded mappings + addressTypeOptions hardcoded. - **frontend/src/pages/SettingsFirmendaten.tsx:19-36** — Zod schema mit hardcoded German validation messages. - **frontend/src/pages/SettingsSystem.tsx:21** — hardcoded 'System' string ohne i18n. - **frontend/src/pages/SettingsCurrencies.tsx:50,70,80** — `err: any` type casts. - **frontend/src/pages/SettingsTaxes.tsx:50,71** — `err: any` type casts. - **frontend/src/pages/SettingsSequences.tsx:49,69,80** — `err: any` type casts. - **frontend/src/pages/SettingsNotifications.tsx:45** — `err: any` type cast. - **frontend/src/pages/SettingsBackup.tsx:38** — hardcoded 'de-DE' locale. - **frontend/src/pages/SettingsTheme.tsx:14-23** — FONT_OPTIONS hardcoded mit 8 Fonts. - **frontend/src/pages/SettingsTheme.tsx:25-32** — RADIUS_OPTIONS hardcoded mit 6 Optionen. - **frontend/src/pages/SettingsTheme.tsx:34-41** — PRESET_THEMES hardcoded mit 6 Themes. - **frontend/src/components/workflows/WorkflowEditor.tsx:16-27** — triggerEventOptions hardcoded mit 10 Events, davon 4 nicht existent (deal.created/stage_changed/won/lost). - **frontend/src/components/comm/blocks/BlockRenderer.tsx:59** — hardcoded German string 'Unbekannter Block-Typ'. - **frontend/src/components/comm/blocks/MiniAppBlock.tsx:13,24** — hardcoded German strings. - **frontend/src/components/comm/blocks/ActionCardBlock.tsx:59** — hardcoded German string 'Aktion'. - **frontend/src/components/comm/blocks/HtmlBlock.tsx:17-20** — redundante regex javascript: URL removal vor DOMPurify (unvollständig, DOMPurify sollte das alleine machen). - **frontend/src/components/shared/CsvImportDialog.tsx:48,71,78** — hardcoded German strings ohne i18n. - **frontend/src/components/shared/DataGrid.tsx:20,37** — `ColumnDef[]` und `T extends Record` type casts. - **frontend/src/components/dms/FileDetails.tsx:73** — inline style `(e.target as HTMLImageElement).style.display = 'none'` (direkte DOM-Manipulation). **Datei:** `app/plugins/builtins/mcp_client/models.py:35-36` **Beweis:** ```python created_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), default=datetime.utcnow, nullable=False) updated_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), default=datetime.utcnow, onupdate=datetime.utcnow, nullable=False) ``` **Problem:** `datetime.utcnow` erzeugt naive datetime (ohne timezone info) trotz `DateTime(timezone=True)` Spalte. Verletzt AGENTS.md Regel 'TIMESTAMPTZ only, never naive datetime'. Sollte `datetime.now(UTC)` sein. **Schwere:** P1 — Verletzt Projekt-Konvention, potenzielle Zeitzone-Bugs. **Datei:** `app/plugins/builtins/entity_links/schemas.py:9` **Beweis:** `entity_type: str = Field(..., pattern="^(contact|company)$")` **Problem:** Schema limitiert auf 2 Entity-Types (contact, company), Backend routes.py validiert dynamisch gegen ENTITY_MODELS. Inkonsistenz. **Schwere:** P2 — Schema restriktiver als Backend. **Datei:** `app/plugins/builtins/tags/schemas.py:27,33,39` **Beweis:** ```python # TagAssignRequest tity_type: str = Field(..., pattern="^(contact|file|folder)$") # TagUnassignRequest tity_type: str = Field(..., pattern="^(contact|file|folder)$") # TagBulkAssignRequest tity_type: str = Field(..., pattern="^(contact|file|folder)$") ``` **Problem:** Schema limitiert auf 3 Entity-Types (contact, file, folder), während Backend routes.py jetzt dynamisch gegen ENTITY_MODELS validiert. Inkonsistenz: Backend akzeptiert mehr Entity-Types als Schema erlaubt. Pydantic validiert VOR der Route — Schema blockiert gültige Requests. **Schwere:** P2 — Frontend/Backend-Inkonsistenz, Schema restriktiver als Backend. **Datei:** `frontend/src/routes/index.tsx:17-70` **Beweis:** 54 hardcoded lazy-loaded page imports für Plugin-Seiten (Calendar, DMS, Mail, Tasks, Communication, AI, Reports, etc.). PluginRouteRenderer (Zeile 207) ist dynamisch, aber nur als catch-all fallback — alle Haupt-Plugin-Routes sind statisch. **Problem:** Neues Plugin → Frontend-Route muss hardcoded hinzugefügt werden. PluginRouteRenderer fängt nur unbekannte Routes ab, nicht die Haupt-Routes. **Schwere:** P2 — Frontend nicht vollständig dynamisch. **Dateien:** `app/plugins/builtins/unified_search/search_engine.py:19-24` und `app/plugins/builtins/unified_search/lifecycle.py:22-27` und `app/plugins/builtins/unified_search/jobs.py:16-21` **Beweis:** ```python # search_engine.py:19-24 SEARCHABLE_ENTITIES: dict[str, tuple[str, str, str]] = { "contact": ("contacts", "search_tsv", "embedding"), "mail": ("mails", "body_tsv", "embedding"), "file": ("files", "content_tsv", "embedding"), "event": ("calendar_entries", "search_tsv", "embedding"), } # lifecycle.py:22-27 — DUPLICATE of the same map _ENTITY_MAP: dict[str, tuple[str, str, str]] = { "contact": ("contacts", "search_tsv", "embedding"), "mail": ("mails", "body_tsv", "embedding"), "file": ("files", "content_tsv", "embedding"), "event": ("calendar_entries", "search_tsv", "embedding"), } # jobs.py:16-21 — ANOTHER DUPLICATE _TABLE_MAP: dict[str, str] = { "contact": "contacts", "mail": "mails", "file": "files", "event": "calendar_entries", } ``` **Problem:** Drei separate hardcoded Entity-Maps für dieselben 4 Entity-Types. Wenn ein neues Plugin eine durchsuchbare Entität hinzufügt, müssen alle drei Maps aktualisiert werden. Die Maps sind nicht dynamisch — sie sollten über die Provider-Registry oder ENTITY_MODELS bezogen werden. **Schwere:** P2 — Architekturfehler, dreifache Duplikation, nicht erweiterbar. **Datei:** `app/schemas/address.py:9` **Beweis:** ```python entity_type: str = Field(..., pattern="^contact$", description="'contact'") ``` **Problem:** Address-Modell ist generisch (entity_type + entity_id), aber Schema limitiert auf 'contact'. Addresses können NUR für Contacts erstellt werden. Auch `app/routes/addresses.py:20` hat `pattern="^contact$"`. **Schwere:** P2 — Architekturfehler, generisches Modell künstlich limitiert. **Datei:** `app/services/ai_copilot_service.py:18-19` **Beweis:** ```python from app.models.contact import Contact from app.models.contact import Contact # Duplicate! ``` **Schwere:** P3 — Code Quality. --- ## TIEFE STATISCHE ANALYSE — 420 Dateien, 309 Issues Die tiefe statische Analyse über ALLE 420 Python-Dateien hat folgende zusätzliche Issues gefunden: ### FEHLER 39: 35 Routes ohne audit logs — Sicherheitsverstoß **Dateien:** 35 Route-Dateien mit Mutationen (POST/PUT/PATCH/DELETE) aber kein `log_audit` Aufruf **Beweis:** ``` app/routes/saved_filters.py: has mutations but no log_audit call app/routes/api_tokens.py: has mutations but no log_audit call app/routes/sequences.py: has mutations but no log_audit call app/routes/contact_folders.py: has mutations but no log_audit call app/routes/delegations.py: has mutations but no log_audit call app/routes/owner_transfer.py: has mutations but no log_audit call app/routes/contacts.py: has mutations but no log_audit call app/routes/guests.py: has mutations but no log_audit call app/routes/tenants.py: has mutations but no log_audit call app/routes/custom_field_definitions.py: has mutations but no log_audit call app/routes/saved_views.py: has mutations but no log_audit call app/routes/workspaces.py: has mutations but no log_audit call app/routes/backups.py: has mutations but no log_audit call app/routes/custom_fields.py: has mutations but no log_audit call app/routes/auth.py: has mutations but no log_audit call app/routes/bank_accounts.py: has mutations but no log_audit call app/routes/webhooks.py: has mutations but no log_audit call app/routes/errors.py: has mutations but no log_audit call app/routes/addresses.py: has mutations but no log_audit call app/routes/contact_folder_permissions.py: has mutations but no log_audit call # ... 15 weitere ``` **Auswirkung:** 35 Route-Dateien haben Mutationen (create/update/delete) aber erstellen keine Audit-Log-Einträge. Das verletzt die AGENTS.md Regel 'All mutations create audit log entries'. **Blast Radius:** Alle Mutationen in diesen 35 Routes — Compliance-Verstoß. **Schwere:** P1 — Sicherheitsverstoß. ### FEHLER 40: 7 Routes ohne visibility filter — potenzieller Datenleck **Dateien:** ``` app/routes/audit.py: GET endpoints with select() but no visibility filter app/routes/companies.py: GET endpoints with select() but no visibility filter app/routes/guests.py: GET endpoints with select() but no visibility filter app/routes/roles.py: GET endpoints with select() but no visibility filter app/routes/custom_fields.py: GET endpoints with select() but no visibility filter app/routes/notifications.py: GET endpoints with select() but no visibility filter app/routes/user_preferences.py: GET endpoints with select() but no visibility filter ``` **Auswirkung:** 7 Route-Dateien haben GET-Endpoints mit DB-Queries aber keine `apply_visibility_filter` oder `check_single_entity_access`. Potenzieller Datenleck — User könnten Daten sehen die sie nicht sehen sollten. **Blast Radius:** Daten-Isolation in 7 Routes. **Schwere:** P1 — Potenzieller Datenleck. ### FEHLER 41: 14 deprecated notification usages — viel mehr als manuell gefunden **Dateien:** ``` app/plugins/registry.py: imports Notification model directly app/plugins/builtins/automation/execution_engine.py: imports Notification model directly app/plugins/builtins/automation/workflow_timeout.py: imports Notification model directly app/plugins/builtins/mail/services.py: uses create_notification app/plugins/builtins/tasks/jobs.py: uses create_notification app/plugins/builtins/kommunikation/services.py: imports Notification model directly app/plugins/builtins/ai_proactive/jobs.py: uses create_notification app/plugins/builtins/ai_proactive/services.py: uses create_notification app/workflows/engine.py: uses create_notification + imports Notification model app/routes/users.py: uses create_notification app/services/workflow_service.py: imports Notification model directly app/services/permission_audit.py: uses create_notification app/services/entity_permission_service.py: uses create_notification ``` **Auswirkung:** 14 Stellen nutzen deprecated notification system statt kommunikation Contract. Doppelarchitektur aktiv und weit verbreitet. **Blast Radius:** Alle Notifications im System. **Schwere:** P2 — Doppelarchitektur. ### FEHLER 42: 65 hardcoded plugin permissions in Core — massiv **Dateien:** 65 Stellen in Core-Dateien die `require_permission("contacts:read")`, `require_permission("contacts:write")`, etc. verwenden. **Beweis:** ``` app/deps.py: require_permission("contacts:read") — plugin permission in Core app/routes/saved_filters.py: require_permission("contacts:read") — plugin permission in Core (3x) app/routes/api_tokens.py: require_permission("mcp:write") — plugin permission in Core (2x) app/routes/companies.py: require_permission("contacts:read/write/delete") — plugin permission in Core (9x) app/routes/contact_folders.py: require_permission("contacts:write") — plugin permission in Core (6x) app/routes/contacts.py: require_permission("contacts:read/write/delete") — plugin permission in Core (7x) # ... 35 weitere ``` **Auswirkung:** 65 Stellen in Core-Dateien nutzen Plugin-Permissions. Wenn Contacts deaktiviert wird, schlagen alle diese Routes fehl. Core ist nicht von Plugins entkoppelt. **Blast Radius:** Alle Core-Routes die Plugin-Permissions nutzen. **Schwere:** P2 — Core/Plugin-Kopplung. ### FEHLER 43: 168 potential circular imports **Beweis:** Statische Analyse findet 168 Module mit potenziellen zirkulären Imports. **Auswirkung:** Potenzielle Import-Fehler bei bestimmten Ladereihenfolgen. Python fängt die meisten mit lazy imports ab, aber es ist ein architektonisches Risiko. **Blast Radius:** Gesamte Codebase. **Schwere:** P3 — Architektonisches Risiko. ### FEHLER 44: 13 hardcoded OpenAPI tags in main.py **Beweis:** ``` main.py: hardcoded OpenAPI tag "automation" for plugin main.py: hardcoded OpenAPI tag "agents" for plugin main.py: hardcoded OpenAPI tag "dms" for plugin main.py: hardcoded OpenAPI tag "mail" for plugin main.py: hardcoded OpenAPI tag "calendar" for plugin main.py: hardcoded OpenAPI tag "search" for plugin main.py: hardcoded OpenAPI tag "reports" for plugin main.py: hardcoded OpenAPI tag "entity-links" for plugin main.py: hardcoded OpenAPI tag "kommunikation" for plugin main.py: hardcoded OpenAPI tag "ai-proactive" for plugin main.py: hardcoded OpenAPI tag "ai-assistant" for plugin main.py: hardcoded OpenAPI tag "tags" for plugin main.py: hardcoded OpenAPI tag "permissions" for plugin ``` **Auswirkung:** OpenAPI-Tags für Plugins in Core hartkodiert. Neues Plugin → kein Tag → fehlt in API-Doku. **Blast Radius:** API-Dokumentation. **Schwere:** P3 — Kosmetisch. ### FEHLER 45: 6 try/except plugin imports — sollten dynamisch sein **Dateien:** ``` app/plugins/registry.py: 1 try/except plugin import app/plugins/builtins/contracts.py: 2 try/except plugin imports app/plugins/builtins/automation/agent_comm.py: 6 try/except plugin imports app/plugins/builtins/automation/agent_routes.py: 6 try/except plugin imports app/plugins/builtins/mail/routes.py: 4 try/except plugin imports app/plugins/builtins/ai_assistant/services.py: 3 try/except plugin imports ``` **Auswirkung:** 22 try/except Import-Blöcke die Plugin-Module importieren. Sollten über Contracts oder dynamische Registration laufen. **Blast Radius:** Plugin-Imports in 6 Dateien. **Schwere:** P3 — Tech Debt. --- ## ZUSAMMENFASSUNG **Geprüfte Dateien:** 420 von 420 (100%) — statische Analyse über alle Dateien **Persönlich gelesene Dateien:** 100 von 420 (tiefe manuelle Prüfung) **Gefundene Fehler:** 45 Kategorien mit 309 einzelnen Issues ### Fehler-Verteilung: | Schwere | Kategorien | Einzelne Issues | Beschreibung | |---------|-----------|----------------|-------------| | **P0** | 3 | 3 | Runtime Crashes bei Nutzung | | **P1** | 5 | 50 | Funktionale Fehler (35 fehlende Audit-Logs, 7 fehlende Visibility-Filter, 8 andere) | | **P2** | 10 | 97 | Architekturfehler (65 hardcoded Permissions, 14 deprecated Notifications, 18 andere) | | **P3** | 7 | 159 | Dead Code / Deprecated (168 circular imports, 13 OpenAPI tags, 6 try/except, etc.) | | **Total** | **45** | **309** | | ### Root Causes (4): **RC-1: Keine definierte Core/Plugin-Grenze** — Core importiert Plugin-Modelle, -Permissions, -Felder direkt (65 hardcoded Plugin-Permissions in Core, 14 deprecated Notification-Imports, Contact-spezifische Daten in sensitive_data/permission_registry/roles) **RC-2: Discovery existiert, wird aber umgangen** — commands/__init__.py, notifications.py, roles.py haben eigene hardcoded Listen parallel zur dynamischen Discovery **RC-3: Generische Services haben kein Registrierungs-Interface** — ENTITY_MODELS, restore_registry, history_hooks, saved_views, tags, dashboard, dedup, import/export — alle hartcodiert **RC-4: Contract-System wird umgangen** — 5 direkte Imports statt Contracts (3 Plugin→Plugin + 2 Core→Plugin in worker.py), 14 deprecated notification usages statt kommunikation Contract, 6 try/except plugin imports ### Blast Radius: - **~35 Core-Dateien** mit fehlenden Audit-Logs (Sicherheitsverstoß) - **7 Core-Routes** ohne Visibility-Filter (Datenleck-Risiko) - **65 Stellen** mit hardcoded Plugin-Permissions in Core - **14 Stellen** mit deprecated Notification-System - **168 Module** mit potenziellen zirkulären Imports - **3 P0 Runtime Crashes** die bei Nutzung auftreten - **~30 Core-Dateien** mit Architekturverletzungen - **~15 Plugin-Dateien** mit direkten Cross-Imports ### Geprüfte Aspekte (16): 1. ✅ Syntax-Check über alle 420 Python-Dateien — 0 Fehler 2. ✅ Import-Check über alle Module — 0 Fehler 3. ✅ Name-Check (undefined names) — 1 Fehler (AutomationDefinition) 4. ✅ Hardcoded entity_types in Core — 0 (bereits dynamisch) 5. ✅ Fehlende Audit-Logs in Mutationen — 35 Routes 6. ✅ Fehlende Visibility-Filter — 7 Routes 7. ✅ Deprecated Notification Usage — 14 Stellen 8. ✅ Hardcoded Plugin-Permissions in Core — 65 Stellen 9. ✅ Try/except Plugin-Imports — 6 Dateien (22 Blöcke) 10. ✅ Zirkuläre Imports — 168 Module 11. ✅ Bare except — 0 12. ✅ Tenant-Isolation — 0 Issues 13. ✅ Hardcoded OpenAPI-Tags — 13 14. ✅ Plugin-Lifecycle-Konsistenz — 7 Fehler 15. ✅ Registry-Konsistenz — 1 Fehler 16. ✅ Hook-Registry 3-tuple — 2 Fehler