Files
leocrm/docs/audit-consolidated-errors.md
T
Agent Zero abbe7a18fc fix(audit): P0-P3 audit fixes — 838 ruff errors → 0, 30 F821 bugs fixed, 118 files changed
- P0: hooks.py 3-tuple fix, trigger_dispatcher Contract, contacts/plugin unregister_actions_by_owner
- P0: 5 test files — check_permission mocks removed, hardcoded DB credential → env var
- P1: attachment_service DmsFile via Contract helper, restore_registry/history_hooks dedup
- P1: mail/plugin restore unregister, mcp_client datetime.now(UTC), saved_views/filters patterns
- P1: ProtectedRoute fail-closed, 13 test assertion fixes (bcrypt, DB-URLs, SECRET_KEYs)
- P2: deprecated notifications → post_system_message (3 files), forgejo Base, report_generator lazy import
- P2: webhooks permissions, deps.py/roles.py plugin perms removed, import_export default
- P2: address/tags/entity_links patterns removed, worker.py Contract-Umgehungen fixed
- P2: 28 frontend TODOs (hardcoded constants, deprecated notification API)
- P3: dead code, duplicates, deprecated imports, private attr, __import__ inline
- P3: 8 frontend TODOs (LucideIcons, inline styles, XSS, i18n)
- ruff: 838 → 0 (612 auto-fix + 246 manual + 27 F821 regression fix)
- F821: 30 → 0 (AutomationDefinition, DmsFile, user_id, Path, Any, String)
- Contract-Umgehungen: 2 neue gefunden (worker.py:169, worker.py:280) und gefixt
2026-08-16 01:17:18 +02:00

27 KiB

Konsolidierte Fehlerliste — LeoCRM Architektur-Audit

Datum: 2026-08-15 Dateien geprüft: 750 von 1052 (siehe docs/audit-tracker.md) Verbleibend: 302 Dateien (hauptsächlich Alembic-Migrationen + Test-Dateien)


Zusammenfassung

Schwere Backend Frontend Tests Total
P0 3 0 5 8
P1 10 1 26 37
P2 25 25 21 71
P3 20 30 8 58
Total 58 56 60 174

P0 — Runtime Crashes / Security (8)

P0-1: hooks.py:83 — unregister() _filters 2-tuple CRASH

Datei: app/core/hooks.py:83 Beweis: Beweis:

# Zeile 83: _filters nutzt 2-tuple unpacking, aber register_filter speichert 3-tuple
self._filters[hook_name] = [
    (p, c) for

### P0-2: trigger_dispatcher.py:127 — AutomationDefinition nicht importiert
**Datei:** `app/core/trigger_dispatcher.py:127`
**Beweis:** **Beweis:**
```python
# Zeile 127: AutomationDefinition wird in Query verwendet, aber nie importiert
query = (
    select(AutomationDefinition)  # Nam

### P0-3: contacts/plugin.py:88-90 — clear_actions statt unregister_actions_by_owner
**Datei:** `app/plugins/builtins/contacts/plugin.py:88-90`
**Beweis:** **Beweis:**
```python
hook_reg.clear_actions("contact.after_create")  # Entfernt Hooks anderer Plugins!
hook_reg.clear_actions("contact.after_update")

### P0-T1: test_external_agent_api.py:36-39 — check_permission mocked to True
**Datei:** `tests/test_external_agent_api.py:36-39`
**Beweis:** `@pytest.fixture(autouse=True)`  `patch("app.core.permissions.check_permission", return_value=True)`  alle Tests bypass RBAC

### P0-T2: test_graph_rag.py:39-43 — check_permission mocked to True
**Datei:** `tests/test_graph_rag.py:39-43`
**Beweis:** Same autouse fixture  alle Tests bypass RBAC

### P0-T3: test_agent_memory.py:39-43 — check_permission mocked to True
**Datei:** `tests/test_agent_memory.py:39-43`
**Beweis:** Same autouse fixture  alle Tests bypass RBAC

### P0-T4: test_marketplace.py:45-49 — check_permission mocked to True
**Datei:** `tests/test_marketplace.py:45-49`
**Beweis:** Same autouse fixture  alle Tests bypass RBAC

### P0-T5: test_cross_tenant_standalone.py:33 — Hardcoded DB credential
**Datei:** `tests/test_cross_tenant_standalone.py:33`
**Beweis:** `DB_URL = "postgresql+asyncpg://crm_user:4B6X2wlfbIx-PyaG8kGutsatdLbjdBUI@crm-postgres:5432/crm_db"`  Passwort im Source Code

---

## P1 — Funktionale Fehler (37)

### P1-4: attachment_service.py:48 — DmsFile type hint used but not imported
**Datei:** `app/services/attachment_service.py:48`
**Beweis:** **Beweis:**
```python
def _entity_attachment_to_dict(ea: EntityAttachment, dms_file: DmsFile | None = None) -> dict[str, Any]:

DmsFile wird als

P1-5: restore_registry.py — register_default_entities registriert Contact

Datei: app/core/restore_registry.py:113-195 Beweis: Beweis: register_default_entities() registriert Contact RestoreConfig. ContactsPlugin.on_activate() registriert AUCH Contact RestoreConfig. →

P1-6: history_hooks.py — register_default_history_hooks registriert Contact ohne owner_tag

Datei: app/core/history_hooks.py:140 Beweis: Beweis: register_default_history_hooks() registriert Contact hooks ohne owner_tag. ContactsPlugin.on_activate() registriert AUCH Contact hooks

P1-8: mail/plugin.py — on_deactivate fehlt unregister_actions_by_owner und restore unregister

Datei: app/plugins/builtins/mail/plugin.py Beweis: Beweis: mail/plugin.py on_deactivate hat KEIN unregister_actions_by_owner für history hooks und KEIN unregister für restore config. *Auswirkung:

P1-10: saved_views.py:62 — pattern validation hardcoded

Datei: app/routes/saved_views.py:62 Beweis: Beweis:

entity_type: str | None = Query(None, pattern="^(contacts|mail|calendar|dms)$"),

Obwohl _validate_entity_type() gegen ENTI

P1-11: saved_filters.py:62 — pattern validation hardcoded

Datei: app/routes/saved_filters.py:62 Beweis: Beweis:

entity_type: str | None = Query(None, pattern="^(contacts|mail|calendar|dms)$"),

Gleiches Problem wie saved_views.py. **Ausw

P1-30: mail/plugin.py:194-200 — on_deactivate fehlt restore + history unregister

Datei: app/plugins/builtins/mail/plugin.py:194-200 Beweis: Beweis:

async def on_deactivate(self, db, service_container, event_bus) -> None:
    # Contract abmelden
    from app.plugins.builtins.c

### P1-31: frontend/src/api/tags.ts:12 — EntityType hardcoded und inkonsistent mit Backend
**Datei:** `frontend/src/api/tags.ts:12`
**Beweis:** **Beweis:**
```typescript
export type EntityType = 'contact' | 'file' | 'calendar_entry';

Backend validiert dynamisch gegen ENTITY_MODELS (contact

P1-47: mail/plugin.py:194-217 — on_deactivate FEHLT restore unregister (P1)

Datei: app/plugins/builtins/mail/plugin.py:194-217 Beweis: Beweis: on_deactivate deregistriert history hooks (Zeile 213-215) aber hat KEIN get_restore_registry().unregister("mail"). Problem: Mail res

P1-58: mcp_client/models.py:35-36 — Naive datetime statt UTC (P1)

Datei: app/plugins/builtins/mcp_client/models.py:35-36 Beweis: Beweis: datetime.utcnow (naive) statt datetime.now(UTC).

P1-F1: ProtectedRoute.tsx:22-24 — Fail-open bei leeren Permissions

Datei: frontend/src/components/common/ProtectedRoute.tsx:22-24 Beweis: if (perms.length === 0) { return <>{children}</>; } — access erlaubt wenn permissions nicht geladen

P1-T1: test_ai_copilot.py:45-47,77-79

Beweis: Tests silently pass bei 403 — assert status_code in (200, 403) then return

P1-T2: test_user_service.py:38-39,82-100

Beweis: Non-deterministic assertions, try/except pass

P1-T3: test_backup_service.py:37-38,50-51,63

Beweis: Overly permissive assertions (4 status codes)

P1-T4: test_mcp_server.py:62,127

Beweis: assert success in (True, False) — immer True

P1-T5: test_saved_filters.py (entire file)

Beweis: Missing cross-tenant isolation + RBAC tests

P1-T6: test_tasks.py (entire file)

Beweis: Missing cross-tenant isolation + RBAC tests

P1-T7: test_dashboard.py (entire file)

Beweis: Missing cross-tenant isolation + RBAC tests

P1-T8: test_custom_fields.py (entire file)

Beweis: Missing cross-tenant isolation + RBAC tests

P1-T9: test_calendar.py (first 200 lines)

Beweis: Missing cross-tenant isolation test

P1-T10: test_workflows.py (entire file)

Beweis: Missing RBAC + tenant isolation tests

P1-T11: test_notifications.py (entire file)

Beweis: Missing tenant isolation + RBAC tests

P1-T12: test_companies.py (entire file)

Beweis: Missing visibility filter test

P1-T13: test_contacts.py (entire file)

Beweis: Missing visibility filter test

P1-T14: test_dms.py + test_dms_coverage.py + test_dms_errors.py

Beweis: Missing cross-tenant isolation tests

P1-T15: test_entity_links.py (entire file)

Beweis: Missing cross-tenant test

P1-T16: test_tags.py (entire file)

Beweis: Missing cross-tenant + RBAC test


P2 — Architekturfehler (71)

P2-7: entity_permission_service.py:59-61 — Contact hardcoded in ENTITY_MODELS

Datei: app/services/entity_permission_service.py:59-61 Beweis: Beweis:

ENTITY_MODELS: dict[str, type] = {
    "contact": Contact,
    "contacts": Contact,
    "company": Contact,
    # ...
}

Cont

P2-9: hooks.py:52-53 — Type-Annotationen falsch

Datei: app/core/hooks.py:52-53 Beweis: Beweis:

cls._instance._actions: dict[str, list[tuple[int, Callable]]] = defaultdict(list)  # sollte tuple[int, Callable, str | None]
cls

### P2-12: saved_views.py:60 — require_permission("contacts:read") hardcoded
**Datei:** `app/routes/saved_views.py:60`
**Beweis:** **Beweis:**
```python
@router.get("", dependencies=[Depends(require_permission("contacts:read"))])

Saved-Views benötigen contacts:read Permissio

P2-13: saved_filters.py:60 — require_permission("contacts:read") hardcoded

Datei: app/routes/saved_filters.py:60 Beweis: Beweis:

@router.get("", dependencies=[Depends(require_permission("contacts:read"))])

Gleiches Problem wie saved_views.py. **Auswirku

P2-14: sensitive_data.py:83-98 — DATA_EXPOSURE_POLICY hat Contact-spezifische Felder

Datei: app/core/sensitive_data.py:83-98 Beweis: Beweis:

DATA_EXPOSURE_POLICY: dict[str, dict[str, dict[str, bool]]] = {
    "contact": {
        "code": _EXPORT_ONLY,
        "accounti

### P2-15: permission_registry.py:86-122 — CORE_FIELD_DEFINITIONS hat Contact-spezifische Felder
**Datei:** `app/core/permission_registry.py:86-122`
**Beweis:** **Beweis:**
~40 Contact-spezifische Felddefinitionen hartkodiert in Core.
**Auswirkung:** Core enthält CRM-spezifische Felddefinitionen. Neue Contact-

### P2-16: sensitive_data.py:24-48 — SENSITIVE_FIELDS hat Contact/Mail-spezifische Felder
**Datei:** `app/core/sensitive_data.py:24-48`
**Beweis:** **Beweis:**
```python
SENSITIVE_FIELDS: dict[str, set[str]] = {
    "contact": {"password_hash", "smtp_password", "imap_password", ...},
    "mail_acc

### P2-23: report_generator/plugin.py:9 — top-level import of jobs module
**Datei:** `app/plugins/builtins/report_generator/plugin.py:9`
**Beweis:** **Beweis:**
```python
from app.plugins.builtins.report_generator import jobs  # noqa: F401

Top-Level-Import von jobs-Modul hat Side-Effects (regis

P2-24: base.py:81 — unregister_all_for_plugin nutzt self Heuristik

Datei: app/plugins/base.py:81 Beweis: Beweis:

get_hook_registry().unregister_all_for_plugin(self.manifest.name)

unregister_all_for_plugin nutzt `callback.self.manif

P2-26: deps.py:21-36 — _WRITE_PERMISSIONS hardcoded mit Plugin-Permissions

Datei: app/deps.py:21-36 Beweis: Beweis:

_WRITE_PERMISSIONS = [
    "contacts:write",
    "contacts:create",
    # ...
]

contacts:write und contacts:create sind

P2-27: workflow_service.py:13 — importiert deprecated Notification model

Datei: app/services/workflow_service.py:13 Beweis: Beweis:

from app.models.notification import Notification

Workflow-Service nutzt deprecated Notification model statt kommunikation Co

P2-28: dashboard.py:14,61-93 — hardcoded Contact counts, kein Plugin-Beitrag möglich

Datei: app/routes/dashboard.py:14,61-93 Beweis: Beweis:

from app.models.contact import Contact  # Core→Contact (Plugin-Entity)
# ...
contact_query = select(func.count(Contact.id)).wher

### P2-29: import_export.py:40 — entity_type default 'companies' hardcoded
**Datei:** `app/routes/import_export.py:40`
**Beweis:** **Beweis:**
```python
entity_type: str = Form("companies"),

Import/Export unterstützt nur 'companies' und 'contacts' (beide Contact-Modell). Kein

P2-32: conftest.py:41-53 — hardcoded Core-Model imports trotz dynamischer Discovery

Datei: tests/conftest.py:41-53 Beweis: Beweis:

from app.models.ai_conversation import AIConversation, AIMessage  # noqa: F401
from app.models.contact import Contact, ContactPe

### P2-33: roles.py:27-50 — SYSTEM_PERMISSIONS hardcoded mit Plugin-Permissions
**Datei:** `app/routes/roles.py:27-50`
**Beweis:** **Beweis:**
```python
SYSTEM_PERMISSIONS: list[dict[str, str]] = [
    {"key": "contacts:read", "label": "Contacts: Read", "category": "system"},
    

### P2-34: users.py:15 — importiert create_notification (deprecated)
**Datei:** `app/routes/users.py:15`
**Beweis:** **Beweis:**
```python
from app.core.notifications import create_notification

User-Route nutzt deprecated create_notification statt `post_system_

P2-35: workflows/engine.py:122-130 — erstellt Notification model direkt

Datei: app/workflows/engine.py:122-130 Beweis: Beweis:

notification = Notification(
    tenant_id=self.tenant_id,
    user_id=uuid.UUID(user_id),
    type=config.get("notification_typ

### P2-38: webhooks.py:26,49 — require_permission('automation:read/write') für Core-Webhooks
**Datei:** `app/routes/webhooks.py:26,49`
**Beweis:** **Beweis:**
```python
dependencies=[Depends(require_permission("automation:read"))]
dependencies=[Depends(require_permission("automation:write"))]

P2-51: address.py:9 — Hardcoded pattern="^contact$" (P2)

Datei: app/schemas/address.py:9 und app/routes/addresses.py:20 Beweis: Beweis:

entity_type: str = Field(..., pattern="^contact$", description="'contact'")

Problem: Address-Modell ist generisch (entit

P2-54: frontend routes/index.tsx — Hardcoded Plugin-Routes (P2)

Datei: frontend/src/routes/index.tsx:17-70 Beweis: Beweis: 54 hardcoded lazy-loaded page imports für Plugin-Seiten. PluginRouteRenderer (Zeile 207) ist dynamisch als catch-all, aber alle Haupt-Rout

P2-55: tags/schemas.py:27,33,39 — Hardcoded entity_type pattern (P2)

Datei: app/plugins/builtins/tags/schemas.py:27,33,39 Beweis: Beweis: pattern="^(contact|file|folder)$" in TagAssignRequest, TagUnassignRequest, TagBulkAssignRequest. Problem: Schema limitiert auf 3 Ent

P2-56: entity_links/schemas.py:9 — Hardcoded entity_type pattern (P2)

Datei: app/plugins/builtins/entity_links/schemas.py:9 Beweis: Beweis: entity_type: str = Field(..., pattern="^(contact|company)$") Problem: Schema limitiert auf 2 Entity-Types, Backend validiert dynamis

P2-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: Beweis:

from sqlalchemy.orm import declarative_base
Base = declarative_base()  # Eigenes Base, nicht app.core.db.Base!

Problem:

P2-F1: frontend/src/routes/index.tsx:17-70

Beweis: 54 hardcoded lazy-loaded Plugin-Routes

P2-F2: frontend/src/pages/Settings.tsx:18-29

Beweis: 10 hardcoded settings nav items

P2-F3: frontend/src/api/search.ts:40-47

Beweis: ENTITY_URL_MAP hardcoded mit 6 Entity-Types

P2-F4: frontend/src/components/search/CommandPalette.tsx:12-39

Beweis: TYPE_LABELS + TYPE_ICON_CLASSES + typeIcon() hardcoded

P2-F5: frontend/src/pages/GlobalSearchResults.tsx:15-22

Beweis: TYPE_LABELS hardcoded (dupliziert)

P2-F6: frontend/src/api/tags.ts:12

Beweis: EntityType hardcoded, inkonsistent mit Backend

P2-F7: frontend/src/components/notifications/NotificationDropdown.tsx

Beweis: Nutzt alte /notifications API statt kommunikation

P2-F8: frontend/src/components/layout/NotificationBell.tsx:13

Beweis: Nutzt useUnreadNotificationCount von /notifications

P2-F9: frontend/src/api/contacts.ts + unifiedContacts.ts

Beweis: Dual API client (legacy + neu)

P2-F10: frontend/src/components/contacts/FilterPanel.tsx:22-80

Beweis: FIELD_DEFS hardcoded ~40 Contact-Felder

P2-F11: frontend/src/components/contacts/SortPanel.tsx:22-69

Beweis: SORT_FIELDS hardcoded (dupliziert)

P2-F12: frontend/src/components/contacts/GroupPanel.tsx:22-68

Beweis: GROUP_FIELDS hardcoded (dupliziert, triple-dup)

P2-F13: frontend/src/components/mail/MailFolderTree.tsx:22-43

Beweis: FOLDER_NAME_MAP hardcoded 20 IMAP names

P2-F14: frontend/src/components/mail/MailFilterPanel.tsx:22-44

Beweis: FIELD_DEFS hardcoded 10 Mail-Felder

P2-F15: frontend/src/components/mail/MailSortPanel.tsx:19-27

Beweis: SORT_FIELDS hardcoded (dupliziert)

P2-F16: frontend/src/components/mail/MailGroupPanel.tsx:19-29

Beweis: GROUP_FIELDS hardcoded (dupliziert, triple-dup)

P2-F17: frontend/src/components/dashboard/DashboardWidgetLoader.tsx:11-21

Beweis: widgetRegistry hardcoded 3 Widgets

P2-F18: frontend/src/components/contacts/ContactList.tsx:28-80

Beweis: ALL_COLUMNS hardcoded

P2-F19: frontend/src/api/types.ts:12-22

Beweis: Contact interface legacy (first_name/last_name)

P2-F20: frontend/src/pages/SettingsWebhooks.tsx:38-56

Beweis: AVAILABLE_EVENTS hardcoded, 6 Events existieren nicht (deal., note.)

P2-F21: frontend/src/components/workflows/WorkflowEditor.tsx:16-27

Beweis: triggerEventOptions hardcoded, 4 deal.* Events existieren nicht

P2-F22: frontend/src/pages/SettingsUsers.tsx:19-25

Beweis: LEGACY_ROLES hardcoded 5 Rollen

P2-F23: frontend/src/pages/SettingsMenuOrder.tsx:34-46

Beweis: DEFAULT_ORDER hardcoded 11 Items

P2-F24: frontend/src/pages/Trash.tsx:17

Beweis: ENTITY_TYPES hardcoded 5 Types

P2-F25: frontend/src/pages/AgentDashboard.tsx:50-61

Beweis: commonModels hardcoded 10 Modellnamen

P2-T1: test_cross_tenant_security.py:98

Beweis: Invalid bcrypt hash $2b$12$testhash

P2-T2: test_cross_tenant_security_v2.py:132

Beweis: Same invalid hash

P2-T3: test_cross_tenant_standalone.py:73

Beweis: Same invalid hash

P2-T4: test_workspaces.py:42, test_api_tokens.py:33

Beweis: password_hash="dummy" — not valid bcrypt

P2-T5: test_tags.py:138,165,194

Beweis: Random UUIDs für non-existent entities

P2-T6: test_ai_proactive.py:146-150

Beweis: Test grants is_system_admin to bypass permissions

P2-T7: test_commands.py:140-148

Beweis: Wildcard permissions *: * bypass real RBAC

P2-T8: test_mcp_client.py:41

Beweis: API token returned in plaintext in response

P2-T9: test_cross_tenant_security.py:40

Beweis: Hardcoded DB URL with default password

P2-T10: test_cross_tenant_security_v2.py:47-56

Beweis: Hardcoded DB URLs with credentials

P2-T11: test_cross_tenant_security_v2.py:34-37

Beweis: Hardcoded SECRET_KEY

P2-T12: test_cross_tenant_standalone.py:18-21

Beweis: Hardcoded SECRET_KEY

P2-T13: test_no_legacy_tenant_var.py:17-20

Beweis: Hardcoded SECRET_KEY

P2-T14: test_entity_links.py:105

Beweis: Variable named contact_id but used as company entity

P2-T15: test_external_agent_api.py:92-101

Beweis: Manually constructed user with is_system_admin: True

P2-T16: test_graph_rag.py (similar)

Beweis: Same manually constructed user

P2-T17: test_agent_memory.py (similar)

Beweis: Same manually constructed user

P2-T18: test_marketplace.py (similar)

Beweis: Same manually constructed user

P2-T19: frontend/src/pages/SettingsRechte.tsx:37-75

Beweis: PermissionLevelBadge + PrincipalTypeBadge hardcoded

P2-T20: frontend/src/pages/ProactiveAISettings.tsx:3-17

Beweis: categoryLabels + modelOptions hardcoded

P2-T21: frontend/src/pages/CustomFields.tsx:40-43

Beweis: ENTITY_OPTIONS hardcoded (contact, company)


P3 — Dead Code / Code Quality / Deprecated (58)

P3-19: registry.py:36 — _mounted_routes ist Dead Code

Datei: app/plugins/registry.py:36 Beweis: Beweis: self._mounted_routes: dict[str, list[Any]] = {} wird initialisiert aber nie befüllt. Route-Removal-Logik wurde entfernt (Gate-Modell dok

P3-20: ai_assistant/plugin.py:96-98 — direkter Import von kommunikation.contracts

Datei: app/plugins/builtins/ai_assistant/plugin.py:96-98 Beweis: Beweis:

from app.plugins.builtins.kommunikation.contracts import (
    get_participant_registry,
)

Bewertung: Deklariert in `dep

P3-21: system_notif/plugin.py:161 — direkter Import von kommunikation.contracts

Datei: app/plugins/builtins/system_notif/plugin.py:161 Beweis: Beweis:

from app.plugins.builtins.kommunikation.contracts import create_plugin_room, send_message

Bewertung: Deklariert in `depe

P3-22: graph_rag/plugin.py:44,57 — direkter Import von unified_search.contracts

Datei: app/plugins/builtins/graph_rag/plugin.py:44,57 Beweis: Beweis:

from app.plugins.builtins.unified_search.contracts import get_search_registry

Bewertung: Deklariert in `dependencies=["u

P3-25: entity_permission_service.py:30 — importiert create_notification

Datei: app/services/entity_permission_service.py:30 Beweis: Beweis:

from app.core.notifications import create_notification

create_notification ist deprecated und nutzt jetzt `get_contract("k

P3-36: schemas/common.py:22-58 — Notification schemas für deprecated system

Datei: app/schemas/common.py:22-58 Beweis: Beweis:

class NotificationResponse(BaseModel): ...
class NotificationListResponse(BaseModel): ...
class UnreadCountResponse(BaseModel): 

### P3-37: custom_fields.py:42 — greift auf registry._plugins direkt zu
**Datei:** `app/routes/custom_fields.py:42`
**Beweis:** **Beweis:**
```python
for plugin in registry._plugins.values():

Greift auf privates Attribut _plugins zu statt öffentlichen API (`list_discovere

P3-48: entity_attachment.py:45-49 — import inline statt func import (P3)

Datei: app/models/entity_attachment.py:45-49 Beweis: Beweis:

created_at: Mapped[datetime] = mapped_column(
    DateTime(timezone=True), nullable=False, server_default=__import__('sqlalchemy

### P3-49: ai_copilot_service.py:18-19 — Duplicate Contact import (P3)
**Datei:** `app/services/ai_copilot_service.py:18-19`
**Beweis:** **Beweis:**
```python
from app.models.contact import Contact
from app.models.contact import Contact  # Duplicate!

P3-50: system_settings.py:44-54 — Duplicate field definitions (P3)

Datei: app/schemas/system_settings.py:44-54 Beweis: Beweis:

class SystemSettingsResponse(BaseModel):
    # Zeilen 45-48:
    tax_number: str | None = None
    vat_id: str | None = None
   

### P3-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:** **Beweis:**
```python
from app.models.contact import Contact, ContactPerson
from app.models.contact import Contact, ContactPerson  # Duplicate!

P3-F1: frontend/src/pages/Settings.tsx:5

Beweis: import * as LucideIcons lädt ALLE Icons

P3-F2: frontend/src/pages/Settings.tsx:38

Beweis: (LucideIcons as any)[p.icon] — any type cast

P3-F3: frontend/src/components/contacts/ContactDetail.tsx:11

Beweis: import * as LucideIcons lädt ALLE Icons

P3-F4: frontend/src/components/contacts/ContactDetail.tsx:67-74

Beweis: 8x (contact as any) type casts

P3-F5: frontend/src/components/ai/ChatWindow.tsx:39-41

Beweis: inline styles für animationDelay

P3-F6: frontend/src/components/mail/MailList.tsx:62

Beweis: inline style paddingLeft

P3-F7: frontend/src/components/mail/MailDetail.tsx:49-52

Beweis: iframe HTML rendering (potential XSS)

P3-F8: frontend/src/components/contacts/CustomFieldRenderer.tsx:15-16

Beweis: any types

P3-F9: frontend/src/components/SavedFilters.tsx:22,47,56

Beweis: any types

P3-F10: frontend/src/store/windowStore.ts:8-9

Beweis: ComponentType<any> und Record<string, any>

P3-F11: frontend/src/store/commStore.ts:27,61

Beweis: Record<string, any> und reactions: any[]

P3-F12: frontend/src/hooks/useCommWebSocket.ts:31

Beweis: console.log statt strukturiertem Logger

P3-F13: frontend/src/hooks/useAIContext.ts:5

Beweis: entityData?: any

P3-F14: frontend/src/api/auth.ts:28,36,60

Beweis: any type casts

P3-F15: frontend/src/pages/Login.tsx:41

Beweis: error: any

P3-F16: frontend/src/pages/Workflows.tsx:59,70

Beweis: err: any

P3-F17: frontend/src/pages/AuditLog.tsx:37

Beweis: ColumnDef<AuditLogEntry, any>

P3-F18: frontend/src/components/comm/blocks/BlockRenderer.tsx:59

Beweis: hardcoded German string

P3-F19: frontend/src/components/comm/blocks/MiniAppBlock.tsx:13,24

Beweis: hardcoded German strings

P3-F20: frontend/src/components/comm/blocks/ActionCardBlock.tsx:59

Beweis: hardcoded German string

P3-F21: frontend/src/components/comm/blocks/HtmlBlock.tsx:17-20

Beweis: redundante regex vor DOMPurify

P3-F22: frontend/src/components/shared/CsvImportDialog.tsx:48,71,78

Beweis: hardcoded German strings

P3-F23: frontend/src/components/shared/DataGrid.tsx:20,37

Beweis: any types

P3-F24: frontend/src/components/dms/FileDetails.tsx:73

Beweis: inline style DOM-Manipulation

P3-F25: frontend/src/pages/NoAccessPage.tsx:9-13

Beweis: hardcoded German strings ohne i18n

P3-F26: frontend/src/pages/ActivityTimeline.tsx:25-26

Beweis: hardcoded German strings

P3-F27: frontend/src/pages/SettingsBackup.tsx:38

Beweis: hardcoded de-DE locale

P3-F28: frontend/src/pages/PasswordResetConfirm.tsx:15

Beweis: hardcoded English validation message

P3-F29: frontend/src/pages/DmsTrash.tsx:27-34

Beweis: leere Implementation (Trash-Endpoint fehlt)

P3-F30: frontend/src/pages/GuestLogin.tsx:16 + GuestContacts.tsx:16

Beweis: hardcoded German strings

P3-T1: test_agent_subtasks.py:29-31,35-37

Beweis: Duplicate fixture definition

P3-T2: test_cross_tenant_security.py:187-188

Beweis: Duplicate @pytest.mark.asyncio

P3-T3: test_tenant.py:189

Beweis: Potential KeyError (custom_role)

P3-T4: test_api_documentation.py:10

Beweis: sys.path.insert path manipulation

P3-T5: test_backup_restore.py:15

Beweis: sys.path.insert path manipulation

P3-T6: test_ai_deploy.py:13

Beweis: sys.path.insert path manipulation

P3-T7: test_ai_health_check.py:13

Beweis: sys.path.insert path manipulation

P3-T8: test_dms_coverage.py:13

Beweis: Unused import


Root Causes (4)

  1. Keine definierte Core/Plugin-Grenze — Core importiert Plugin-Modelle, -Permissions, -Felder direkt
  2. Discovery existiert, wird aber umgangen — Schema-Patterns, roles.py SYSTEM_PERMISSIONS, frontend routes
  3. Generische Services haben kein Registrierungs-Interface — ENTITY_MODELS, restore, history, saved_views, tags, dashboard, dedup, import/export
  4. Contract-System wird umgangen — 14 deprecated notifications, direkte Plugin→Plugin Imports, frontend nutzt alte API

Verbleibende 302 Dateien

  • ~100 Alembic-Migrationen (23 stichprobenartig geprüft, alle sauber)
  • ~80 Frontend Test-Dateien (Vitest tests/)
  • ~55 Backend Test-Dateien (vom Test-Audit-Subordinate abgedeckt — 60 Fehler gefunden)
  • ~30 Frontend API-Clients und Hooks (bereits geprüft)
  • ~20 Frontend Stores und Utils (bereits geprüft)
  • ~10 Scripts (8 geprüft)
  • ~7 Frontend E2E Tests

Siehe docs/audit-tracker.md für die vollständige Liste der geprüften und verbleibenden Dateien.