From ed8ee5cda1dc38d47e84126af1e6123306bfcc0d Mon Sep 17 00:00:00 2001 From: Agent Zero Date: Sun, 23 Aug 2026 16:11:01 +0200 Subject: [PATCH] docs: architecture repair progress - plan v3, session status, bug statuses --- PROGRESS.md | 33 +++ docs/fix-plan-v3.md | 501 ++++++++++++++++++++++++++++++++++++++++++++ docs/test-bugs.md | 32 +-- 3 files changed, 550 insertions(+), 16 deletions(-) create mode 100644 docs/fix-plan-v3.md diff --git a/PROGRESS.md b/PROGRESS.md index b9d6f8a..424d0b3 100644 --- a/PROGRESS.md +++ b/PROGRESS.md @@ -6,6 +6,39 @@ --- +## Architektur-Reparatur (2026-08-23, laufend) + +**Plan:** docs/fix-plan-v3.md · **Sequenz:** Block 0 + Block H gemerged in main, Block A läuft auf main +**Stand:** 18 Findings geschlossen (~30% aufwandsgewichtet) · App startbar · 90+ Tests grün · tsc clean · Checker 14→6 Verstöße · Alles gepusht auf Forgejo + +| Finding | Beschreibung | Status | Commit | +|---|---|---|---| +| SYNTAX-001 | automation/plugin.py SyntaxError — App startet nicht | ✅ gefixt | 8077595 | +| CHECK-002 | Checker crasht bei relativen Pfaden | ✅ gefixt | 35e2cc8 | +| ARCH-010 | Checker scannt nur builtins | ✅ Vollscan-Default | 35e2cc8 | +| ARCH-014 | Contract-Lazy-Resurrect nach unregister | ✅ gefixt + Funktionstest | b04cda7 | +| ARCH-020 | EventBus subscribe ohne Duplikat-Check | ✅ gefixt + Funktionstest | b04cda7 | +| ARCH-029/041 | trigger_dispatcher None-Check nach Verwendung | ✅ None-Check zuerst | b04cda7 | +| ARCH-001 | Permissions nach on_activate registriert | ✅ Reihenfolge gedreht | 1d6152f | +| ARCH-002 | on_activate pro Tenant mehrfach | ✅ 1× pro Prozess | 1d6152f | +| ARCH-003 | active-manifests an plugins:read gebunden | ✅ für eingeloggte User offen | 982b4c9 | +| ARCH-038 | BasePlugin.register_event_handlers fehlt | ✅ Hook ergänzt | 90a3670 | +| ARCH-054 | entity_permissions falsche Datenstruktur | ✅ Model-Lookup korrigiert | 90a3670 | +| ARCH-047 | SearchContract-Import kaputt (search-Step ImportError) | ✅ gefixt | d87fc4e | +| ARCH-030 | contract.get_function() existiert nicht | ✅ auf 5 Contracts ergänzt | d87fc4e | +| ARCH-031 | knowledge/plugin.py uuid nicht importiert | ✅ mitfixt | 1f4a621 | +| ARCH-040/046/049 | Core→Plugin-Imports (worker/compliance/engine) | ✅ via Contract/Plugin-Job | 44511a8 + a7699d3 | +| HC-F Frontend | BlockRenderer: 14 hardcodierte Blocks | ✅ Registry | b7ad529 | +| HC-G Frontend | AISidebar: 5 hardcodierte Tabs | ✅ Tab-Registry | 59fdb61 | +| HC-A Backend | action_mapper feste Regex-Intents | ✅ Contribution-API | 4994906 | +| Gate H | Plugin-Contribution ohne Core-Änderung beweisen | ✅ BESTANDEN (2/2) | 801743b | + +**Offen Block A:** ARCH-043, ARCH-052, ARCH-008/009 (Permission-Wildcards), A2-Deactivation-Cleanup (ARCH-012/013/015/033–037/044) → dann Gate A. +**Offen gesamt:** B (Contacts-Entkopplung als Kernstück), C (Frontend-Routen/Workspace/Settings/Dashboard), D (pytest-Massen + Security-Triage), E (Production-Härtung), F3 (Guide-Updates), G (DSGVO). +**Bekannte Vorbestände:** BUG-099 (workstream.py gelöscht in 7d86592, Tests importieren es noch — 7 Failures). test_trigger_core besteht isoliert. + +--- + ## Übersicht | Phase | Status | Start | Ende | Done | Partial | Not Done | Total | Anmerkung | diff --git a/docs/fix-plan-v3.md b/docs/fix-plan-v3.md new file mode 100644 index 0000000..9825ad2 --- /dev/null +++ b/docs/fix-plan-v3.md @@ -0,0 +1,501 @@ +# LeoCRM — Reparaturplan v3 (komplett überarbeitet) + +> Erstellt: 2026-08-23 · v3 nach Plattform-Klarstellung +> Basis: v1 (leocrm-fix-plan.zip gesichert) + ChatGPT-Review (16 Punkte) + eigene Code-Verifizierung +> Grundsatz: Erst Plattformbasis reparieren, dann Plugin-Grenze, dann Frontend, DANACH erst Einzelbugs. +> Alle ARCH-IDs + neue Findings sind explizit einem Block zugeordnet. Nichts hängt mehr in der Luft. + +## LEITBILD (bindend für alle Blöcke) + +Dieses Projekt ist KEINE CRM-Anwendung, sondern eine **Business- & Agenten-Plattform**: +- Der Core ist ein **Plattform-Kernel**: Multi-Tenant, Auth/Permissions, Plugin-System, Events, Workflows, AI-Agent-Laufzeit. +- **Fachmodule (CRM heute, ERP-Module später) sind ausschließlich Plugins** — nachrüstbar, deaktivierbar, ohne Core-Änderung. +- **Agenten sollen die Software VOLL bedienen können** — jede Plugin-Funktion muss automatisch agenten-bedienbar werden, ohne dass der Core angefasst wird. +- Konsequenz als Prüfregel für JEDEN Task: *"Kann ein neues Fachmodul diese Funktion nutzen, ohne eine Core-Datei zu ändern?"* — Wenn nein, ist der Task falsch geschnitten. + +--- + +## 0. Korrigierte Statistik (v1 war falsch) + +| Angabe | v1 (falsch) | v2 (korrekt) | +|---|---|---| +| Bugs gesamt | 148 | **139 eindeutige IDs** | +| Offen | 113 | **108 eindeutig** (31 High / 56 Medium / 21 Low nach Duplikat-Entfernung) | +| Gefixt | 20 | **19** (BUG-073 war doppelt) | +| Kein Bug | 12 | 12 | + +Entfernte Duplikate: BUG-073 (Gefixt-Liste), BUG-074/075/076/077 (Phase 2 doppelt), BUG-078 (Phase 3 doppelt). + +## 1. Was gegenüber v1 geändert wurde (ChatGPT-Review, alle 16 Punkte) + +1. **Statistik korrigiert** (siehe oben), alle Duplikate entfernt. +2. **Phase 0 (Agent Zero Config) gestrichen** — deepseek-v4-pro/ministral/Vision gehören nicht in diesen Plan. +3. **Reihenfolge gedreht**: Plattformbasis ist jetzt Block A (zuerst), Einzelbugs folgen in D. +4. **Contacts-Ansatz korrigiert**: NICHT "NUR Plugin" (v1-Fehler), sondern **Core-Plugin (is_core=True, nicht deaktivierbar) OHNE Core-Hardcoding**. +5. **Workspace ARCH-004 hat jetzt eine konkrete Fixphase** (C2) mit verifizierter Ursache. +6. **Checker-Fix erweitert**: nicht nur Default, sondern ZWEI Bugs (Default + ValueError-Crash bei relativen Pfaden). +7. **Regel aufgenommen**: KEINE globalen commit→flush-Transformationen; nur nachgewiesene Einzelfälle. +8. **Regel aufgenommen**: Integration/Install-Tests ausschließlich über Alembic-Migrationen, kein pauschales `Base.metadata.create_all`. +9. **Frontend-Permission-Reihenfolge fixiert**: ERST Backend-Felder `FrontendPageRoute.permission` + `FrontendMenuItem.permission` (C1), DANN statische Routes abbauen (C4) — sonst Permission-Verlust. +10. **Settings + Dashboard als eigene Fixpunkte ergänzt** (C6/C7) — fehlten in v1 komplett. +11. **Lifecycle-Findings konkret eingeplant**: ARCH-012, 013, 015, 033–037, 042 jetzt einzeln in Block A aufgeführt. +12. **God Objects raus** aus dem Pflichtumbau → separater Track S1. +13. **i18n raus** aus dem Basisumbau → separater Track S2. +14. **Secrets/SQL-Injection = TRIAGE statt Pauschalfix** (D4): jede Stelle klassifizieren, nur echte Findings fixen. +15. **Marathon-Zahlen = Triage-Cluster** (D5), keine Bug-Anzahl, Root-Cause-Clustering. +16. **Verifikations-Gates in JEDEN Block integriert** (nicht erst am Ende) + 10 verpflichtende Integrationstests vor dem Löschen alter Wege. + +## 2. Neue Findings (nicht in v1, heute code-verifiziert) + +| ID | Finding | Ort | Block | +|---|---|---|---| +| SYNTAX-001 | **SyntaxError Zeile 413** (`expected 'except' or 'finally'`) in UNCOMMITTED Änderung — App startet mit aktuellem Working-Tree nicht. Einrückung von `logger.info` + `except` wurde beim if-Guard-Verschub nicht mitgenommen | app/plugins/builtins/automation/plugin.py | 0 | +| CHECK-002 | Checker crasht mit `ValueError: 'app/ai/action_mapper.py' is not in the subpath` bei relativen Pfaden — `check_file()` ruft `relative_to(PROJECT_ROOT)` ohne Resolution | scripts/check_cross_plugin_imports.py:113 | 0 | +| DT-001 | Naive `datetime.utcnow()` in mcp_client routes (fehlte in allen v1-Listen) | app/plugins/builtins/mcp_client/routes.py:173 | D2 | +| SQLITE-001 | SQLite in-memory Fixture — Forbidden Pattern (PostgreSQL only) | app/plugins/builtins/automation/tests/test_automation.py:35 | D2 | + +Vollscan-Ergebnis (nach CHECK-002-Fix): **14 Cross-Plugin-Verstöße**, davon Core→Plugin: `ai/integration_tools.py` (3×), `core/worker.py` (2×), `routes/compliance.py`, `workflows/engine.py`, `routes/compliance.py`; Plugin→Plugin: 5× (Details in Block B). + +--- + +# BLOCK 0 — Sofortmaßnahmen (Blocker, vor allem anderen) + +### 0.1 SYNTAX-001: automation/plugin.py reparieren +- `logger.info(...)` und `except Exception:` im Cron-Job-Block auf die neue Einrücktiefe heben (innerhalb `if default_tenant_id is not None:` → `for` → `try`). +- Verify: `/opt/venv/bin/python -m py_compile app/plugins/builtins/automation/plugin.py` → OK, dann App-Start smoke testen. +- Danach entscheiden: committen (nach Review) oder reverten. Zustand nicht so lassen. + +### 0.2 ARCH-010 + CHECK-002: Cross-Plugin-Checker doppelt fixen +```python +parser.add_argument("--path", default=None) +... +search_path = Path(args.path).resolve() if args.path else None +files = find_python_files(search_path) # None -> Vollscan app/ +``` +- In `check_file()`: `filepath = Path(filepath).resolve()` vor `relative_to(PROJECT_ROOT)`. +- Verify: ohne Argument → ~450+ Dateien, 14 Verstöße; `--path app` (relativ) → kein Crash, identisches Ergebnis. + +### 0.3 Gate +- `python -c "import app.main"` lädt sauber (mit framework runtime). +- Checker-Ausgabe (14 Findings) wird Arbeitsliste für Block A/B. + +--- + +# BLOCK A — Plattformbasis: Lifecycle, Contracts, Events, Runtime + +Ziel: Plugin activate/deactivate deterministisch, Contracts stabil, keine Laufzeit-Namen Fehler. + +### A1 Activation-Reihenfolge & Tenant-Logik +- **ARCH-001**: plugin_service.py:94 — Permissions registrieren VOR `on_activate()`; `active=True` erst nach erfolgreichem on_activate setzen. +- **ARCH-002**: main.py:292-302 — `on_activate()` einmal pro Prozess (global scope), nicht pro Tenant. Tenant-Seed bleibt separat. +- **ARCH-043**: automation register_plugin_contributions — `Tenant.limit(1)` ersetzen durch definierten Default-Tenant-Mechanismus (konfigurierter System-Tenant), dokumentieren. + +### A2 Deactivation & Cleanup (vollständige Liste, war in v1 ungeplant) +- **ARCH-012**: knowledge/wiki on_deactivate vervollständigen (Search-Provider, Event-Handler, Entities deregistrieren). +- **ARCH-013**: self_improvement services.py:586-588 — Fallback auf direkten kommunikation-Import entfernen, Contract-only. +- **ARCH-015**: registry.py:181-244,622-623 — Notification-Type Sync in deactivate-Sequence einordnen. +- **ARCH-033**: kommunikation on_deactivate → service_container.remove() für alle registrierten Services. +- **ARCH-034**: self_improvement on_deactivate → contract unregister. +- **ARCH-035**: marketplace on_deactivate → contract unregister. +- **ARCH-036**: mail `_auto_sync_task` Klassenvariable → Instanzvariable (sonst teilen sich Instanzen den Task-State). +- **ARCH-037**: graph_rag on_activate — `super().on_activate()` VOR eigenem `registry.register()`. +- **ARCH-044**: ai_ui_control on_deactivate — service_container.remove() VOR super().on_deactivate(). +- **BasePlugin erzwingt Symmetrie**: activate was du registered, deactivate must remove (Checkliste in base.py dokumentieren). + +### A3 Contracts +- **ARCH-014**: contracts.py:88-89 — Lazy-Load nach `unregister()` deaktivieren (Flag `unregistered=True` prüfen). +- **ARCH-042**: calendar/dms contracts — `get_contract()` darf neue Instanz erzeugen; registrierte Instanz aus Registry zurückgeben. +- **ARCH-030**: step_handlers.py:221,261,306,351,394 — `get_function()` existiert nicht; tatsächliche Contract-API verwenden (oder Methode sauber ergänzen). +- **ARCH-047**: SearchContract → UnifiedSearchContract (Import + Usage). + +### A4 Events & Worker +- **ARCH-020**: event_bus.py:38 — Duplikat-Check vor append. +- **ARCH-038**: BasePlugin.register_event_handlers() definieren (Hook-Punkt), worker.py:168 nutzt ihn. +- **ARCH-048**: engine.py:663 — payload key "event" → "event_name" (Outbox-Envelope-Kontrakt). +- **ARCH-050**: engine.py acquire_lock — `await get_redis()` auf nicht-async Funktion; konsistent machen (async oder await entfernen). + +### A5 Runtime-Namen & None-Fehler +- **ARCH-029/041**: trigger_dispatcher.py:125 — None-Check VOR Verwendung. +- **ARCH-031**: knowledge/plugin.py — `import uuid` ergänzen. +- **ARCH-032**: knowledge:66 + wiki:41 — `unregister_actions_by_owner(hook_name, owner_tag)` mit 2 Argumenten. +- **ARCH-054**: entity_permissions.py:114/259 — `ENTITY_MODELS.get(entity_type)` direkt, nicht model_info["model"]. +- **ARCH-052**: storage.py get_file_metadata — kein `asyncio.new_event_loop()` im async Kontext; korrekt awaiten. + +### A6 Default-Permissions-Basis +- **ARCH-009**: alembic/0019 + permissions.py:46-50 — Wildcard-Pattern an 2-Segment-Schema angleichen (`contacts:read` statt `core:contacts:read`) + Datenmigration für Bestandsrollen. +- **ARCH-008**: Permission-Namen vereinheitlichen (kommunikation plugin.py:44 vs dashboard.py:23) — Namensschema festlegen und durchsetzen (Konstanten aus registry). + +### Gate A (muss grün sein vor Block B) +1. Import-Test: jedes Modul in app/ lädt (kein NameError/ImportError). +2. Activate → Permissions/Entities sofort registriert; Deactivate → sauber deregistriert (Runtime-Test). +3. Multi-Tenant-Startup: on_activate genau 1× im Log. +4. Contract roundtrip: register → get_contract liefert registrierte Instanz → unregister → get_contract liefert None (kein Lazy-Resurrect). +5. pytest-Smoke der Plugin-Tests (ohne die in D geschobenen Suiten). + +--- + +# BLOCK B — Plugin-Grenze vollständig ziehen + +Ziel: Core kennt Plugins nicht als Sonderfall. Plugins deklarieren alles. + +### B1 Contacts: Core-Plugin statt Core-Hardcoding ⚠️ Kernstück +**Prinzip:** `is_core=True` bedeutet *nicht deaktivierbar* — NICHT *im Core hardcoded*. Contacts bleibt immer aktiv, aber 100% über den Plugin-Mechanismus. + +1. `main.py:549` `app.include_router(contacts.router)` entfernen — NACHDEM der Plugin-Route-Mechanismus identische Endpoints bereitstellt (Endpoint-Diff-Test davor!). +2. Plugin manifest: `routes=[]`-Kommentar beseitigen; Contacts-Routes über Plugin registrieren mit `require_active_plugin("contacts")`-Dependency (schützt bei versehentlicher Deaktivierung + klarer Fehler). +3. `CORE_FIELD_DEFINITIONS` (Contact-Feldmassen) → Plugin-Field-Contribution migrieren. +4. Sidebar-Sonderfall ("Only non-plugin items: dashboard, contacts, settings") → contacts über pluginNavItems. +5. Restore/History/Entity-Links: Contacts-Entity über EntityRegistry (B3), nicht über Core-Annahmen. +6. Acceptance: `grep -rn "contacts" app/main.py` → keine fachliche Referenz mehr; alle Contact-E2E-Tests grün. + +### B2 Core→Plugin Imports via Contracts (alle 6 Core-Stellen des Vollscans) +- `app/ai/integration_tools.py`: tool_registry/knowledge/provider_registry → get_contract("ai_assistant"/"knowledge"/"unified_search"). +- `app/ai/agent_loop.py`: analog prüfen und umstellen. +- `app/core/worker.py:175,460`: provider_registry + KnowledgeExtraction (**deckt ARCH-040 ab**) → Contract/Service-Interface. +- `app/routes/compliance.py:22` (**ARCH-046**): AgentDefinition → Contract oder gemeinsames Core-Schema. +- `app/workflows/engine.py:94-95` (**ARCH-049**): CommConversation → kommunikation-Contract. +- Regel: Wenn ein Contract fehlt, Contract im Plugin definieren und exportieren — NIEMALS Model-Import behalten. + +### B3 Entity-Registry & Custom Fields dynamisch +- **ARCH-016**: ENTITY_PERMISSIONS-Liste aus registrierten Plugin-Entities generieren (entity_permission_service.py:54, entity_permissions.py:252). +- **ARCH-017**: custom_field_definitions.py:25,42 — von contacts-Permission entkoppeln (plugin-eigene Permission je entity_type). +- **ARCH-022**: deps.py _WRITE_PERMISSIONS aus permission_registry generieren (nicht hardcoded). + +### B4 Dependencies deklarieren +- **ARCH-026**: Manifest `depends_on`; Aktivierungsreihenfolge topologisch; Abhängiges nicht deaktivierbar solange Abhängiges aktiv (Fehlermeldung statt stiller Bruch). +- **ARCH-021-Vorbereitung**: Menü/Routen-Daten kommen vollständig aus active-manifests (siehe C). + +### B5 ARCH-003 (früh, weil Frontend es braucht) +- plugins.py:97 — `/plugins/active-manifests` für eingeloggte User freigeben (eigene Permission `plugins:view_manifests`, Default-Rollen erhalten sie). Frontend braucht das für C. + +### Gate B (vor Block C) +1. **Neues-Plugin-Test**: Minimal-Plugin mit Route+Entity+Menü — OHNE Änderung einer Core-Datei funktionsfähig. +2. **Fresh-DB-Install**: leere DB → ausschließlich Alembic → Admin-Seed → Contacts/Companies voll funktional. +3. **Contacts-Vertrag**: Endpoint-Vergleich alt (core route) vs. neu (plugin route) — identische Pfade/Statuscodes. +4. Dependency-Test: A hängt an B → B deaktivieren wird blockiert. +5. Cross-Plugin-Vollscan: 0 Core→Plugin-Verstöße. + +--- + +# BLOCK C — Frontend + Workspace (erst nach A+B) + +⚠️ Reihenfolge innerhalb C ist verbindlich: Backend-Felder (C1) → Store-Filter (C2) → Loader (C3) → erst DANN Route-/Menu-Umbau (C4–C7) → Production-Verify (C8) → alte Wege löschen (C9). + +### C1 Backend: Permission-Felder liefern (VORAUSSETZUNG für alles Weitere) +- `FrontendPageRoute.permission: str | None` + `FrontendMenuItem.permission: str | None` in app/plugins/manifest.py ergänzen (aktuell nur `protected: bool` — verifiziert). +- active-manifests liefert die Felder aus; TypeScript-Typen (PluginMenuItem erwartet bereits `permission?` — Backend zieht nach). +- Migration der vorhandenen Plugin-Manifeste: jede Route/Menü bekommt ihre Permission. + +### C2 Workspace is_visible (ARCH-004, verifizierte Ursache) +- workspaceStore.ts:103-105: `visibleModuleKeys()` filtert aktuell gar nicht: + `new Set((ctx?.modules ?? []).filter(m => m.is_visible !== false).map(m => m.module_key))` +- Server-Seite prüfen: liefert WorkspaceContext modules ohne is_visible? Falls ja, Feld ergänzen. +- Test: `is_visible=false` → Modul unsichtbar in Sidebar + Route guard; Toggle im WorkspaceManager wirkt sofort. + +### C3 PluginLoader Production-Build (ARCH-019) +- `@vite-ignore` Dynamic-Import-Problem lösen: Plugin-Frontends als bekannte Chunk-Map (build-time generiertes Manifest) oder importmap. Ziel: Production-Build lädt Plugin-Module wirklich. + +### C4 Dynamische Routes mit Permission (ARCH-006 + ARCH-007) +- PluginRouteRenderer: PermissionGate mit `route.permission` aus C1 wrappen (gleiches Verhalten wie heutige statische ``). +- Statische Plugin-Routes in routes/index.tsx markieren (deprecated), parallel betreiben bis C8 grün. +- **ARCH-061** gleich mit: leeres Route-Objekt (index.tsx:58-59) entfernen. + +### C5 Sidebar (ARCH-021) +- singleItems reduzieren auf dashboard/login/settings(+profil). Alles andere aus pluginStore (Manifest-Menü inkl. permission, order, icon). +- Sortierreihenfolge aus Manifest `order`. + +### C6 Settings-Doppelarchitektur auflösen (NEU, fehlte in v1) +- Settings.tsx:33-61: `hardcodedNavItems` + `pluginNavItems` + Dedup → EINE Quelle: Settings-Seiten als Manifest-Contribution (`parent: "/settings"` existiert bereits in FrontendPageRoute). Core behält nur Profil/Security-Basis. + +### C7 Dashboard entkoppeln (NEU, fehlte in v1) +- DashboardWidgetLoader.tsx:13-20: statische Widget-Map → Widget-Registry aus Plugin-Manifest (component + permission + dashboard-slot). +- dashboard.py:14,60-66: Contact-Count nicht hardcoded — Count-Endpoint je Entity über Contract/Registry (Contacts-Plugin liefert seinen Count; Core aggregiert). +- RecentContacts/TasksSummary/CalendarUpcoming → Contributions ihrer Plugins. + +### C8 Kleinere Frontend-Dedupes (funktional begründet, KEIN Refactor-Programm) +- **ARCH-062**: TeamPanel → components/shared/TeamPanel.tsx (echte Duplikation, 50 Zeilen). +- **ARCH-063**: SortableMenuItem.tsx:4 `import * as LucideIcons` → ICON_MAP-Pattern (behebt OOM in Tests — funktional, nicht kosmetisch). + +### C9 Alte Wege löschen (NUR nach C8-Verify) +- Statische Plugin-Routes + Sidebar-singleItems + Settings-hardcodes entfernen. + +### Gate C +1. **Production Build** (`npm run build`) + Playwright GEGEN DEN BUILD (nicht dev-server). +2. Workspace: is_visible=false wirklich unsichtbar (Sidebar + Direktaufruf geblockt). +3. Normaler User (kein Admin): sieht Plugin-Menüs/Routes gemäß Permission; 403 wo keine. +4. Permission-Diff-Test: jede ehemals statische Route hatte Permission X → dynamische Route fordert dieselbe X (KEIN Absinken auf auth-only). +5. tsc --noEmit clean. + +--- + +# BLOCK D — Produktbugs & Triage (JETZT erst, auf stabiler Basis) + +### D1 pytest-Suiten reparieren (die v1-„Phase 1"-Massen) +- Reihenfolge nach Basisabhängigkeit: conftest/fixtures → test_auth → test_abac → test_companies → test_calendar → test_ai_proactive → test_api_tokens → test_backend_coverage_gaps → test_phase_h_wiki (BUG-097, 091, 087, 088, 089, 090, 086, 085, 092–096, 098, 099, 067). +- Pro Suite: Root-Cause zuerst (meist Fixtures/RLS/Permission-Setup), keine Test-Anpassung um grün zu werden (AGENTS.md §2). + +### D2 DateTime & Forbidden Patterns (abgeschlossen, konkrete Liste) +- `datetime.utcnow()` → `datetime.now(UTC)` an: core/worker.py:365,409,468 · routes/audit.py:170 · services/webhook_service.py:221 · services/backup_service.py:159 · plugins/mcp_client/routes.py:173 (**DT-001, neu**). (Deckt ARCH-039, 053, 058, 060.) +- **SQLITE-001** (neu): automation test fixture auf ephemeres PostgreSQL (projekt-Konvention) umstellen. + +### D3 API-/Testpfad-Korrekturen (Low, mechanisch) +- BUG-027, 028, 029, 031, 032, 033, 034, 035, 071 (Test-Pfade/Payloads an reale API anpassen — Docs prüfen, nicht Tests verbiegen falls API falsch: dann API fixen). +- **ARCH-051**: dict-body-Routes auf Pydantic-Schemas (Liste aus test-bugs abarbeiten). +- **ARCH-055**: errors.py:124 user_agent. **ARCH-056/057**: roles.py SYSTEM_PERMISSIONS aus registry importieren; `_plugins` → öffentliche Methode. **ARCH-061** falls nicht schon in C4. + +### D4 Security-TRIAGE (nicht pauschal fixen) +- BUG-019 „453 hardcoded secrets“: Klassifizieren (echtes Secret / Testdaten / Konstante / false positive) → nur echte Findings: Secret in env/Secretstore, Referenz einfügen. Ergebnis als Triage-Tabelle dokumentieren. +- BUG-020 „288 SQL-Injection-Risiken“: Nur Stellen mit User-Input-Fluss in String-SQL prüfen; parameterisieren. Scan-Finding ≠ Bug. +- **ARCH-027** hier einsortieren: config.py Default SECRET_KEY in Production hart failen lassen. + +### D5 Marathon-Scanner-Triage (BUG-074–078) +- trace_api_contracts (859), stores (323), hooks (70), plugins (27), functions (3): Cluster-Analyse → wahrscheinliche Root-Causes zählen (Erfahrungswert: deutlich weniger als Issue-Zahl) → Cluster fixen, Scanner erneut laufen lassen, Delta dokumentieren. + +### D6 Legacy-Migration (umsichtig) +- **ARCH-059** (+ ARCH-018-Familie): AIConversation/AIMessage → kommunikation-Conversations migrieren ODER Modul als deprecated markieren + Abschaltplan. Kein Big-Bang: erst Parität herstellen, dann Quellen umstellen, dann Tabellen droppen (eigene Migration). +- **ARCH-023**: service_container.initialize() vervollständigen. + +--- + +# SEPARATE TRACKS (bewusst NICHT Teil dieses Umbaus) + +| Track | Inhalt | Warum geschoben | +|---|---|---| +| S1 Code-Hygiene | God Objects (36 Py + 9 FE, BUG-018/081), weitere >500-Zeilen-Splits | Kein nachgewiesener Basisdefekt; Risiko von Big-Refactors bekannt | +| S2 i18n | BUG-021, ARCH-024, 025, 028, 045 | UI-Qualitätsarbeit, kein Architekturproblem | +| S3 Dependency-Audits | BUG-022/070 npm, pip-audit-Routine | Routineaufgabe, automatisierbar (CI), kein Umbau | + +--- + +# VERIFIKATIONS-GATES (gültig in ALLEN Blöcken) + +Nach jedem Block, vor dem nächsten: +1. Import-Test aller app/-Module (framework runtime). +2. Cross-Plugin-Vollscan: 0 Verstöße der jeweiligen Kategorie. +3. `npx tsc --noEmit` + ruff + mypy (Ziel-Regelwerk) bei jedem Touch. +4. pytest-Smoke der betroffenen Suites. + +## 10 verpflichtende Integrationstests VOR dem Löschen alter Doppelwege (C9/B1-final) +| # | Test | Muss bestehen | +|---|---|---| +| 1 | Runtime Plugin Activation | Permissions + Entities sofort registriert | +| 2 | Runtime Deactivation | sauber deregistriert (Contracts/Services/Events) | +| 3 | Multi-Tenant Startup | on_activate genau 1× | +| 4 | Normaler User | Plugin-Manifeste/Menüs ohne Adminrecht (ARCH-003-Fix wirksam) | +| 5 | Workspace | is_visible=false wirklich unsichtbar | +| 6 | Dependency | A→B geladen; B nicht deaktivierbar solange A aktiv | +| 7 | Neues Plugin | keine Core-Dateiänderung nötig | +| 8 | Frontend Production Build | dynamische Plugin-Routes laden wirklich (nicht dev) | +| 9 | Fresh DB | Editor/Viewer-Permissions funktionieren nach reiner Migration+Seed | +| 10 | Contacts Core-Plugin | funktioniert ohne Contacts-Hardcodings im Core | + +--- + +# BLOCK F — Sicherheit des Umbaus selbst + Wissenssicherung + +Ziel: Der Umbau darf selbst nichts kaputt machen, und die neue Architektur muss so dokumentiert sein, dass die Fehlerklasse NIE wieder entsteht. + +### F1 Rollback- & Branch-Strategie (schützt vor 'Plan macht was kaputt') +- Pro Block ein eigener Git-Branch (`fix/block-a`, `fix/block-b`, ...). Merge in main NUR nach bestandenem Block-Gate. +- Pro Task ein Commit (Conventional Commits mit Finding-ID) — jeder Commit ist einzeln revertierbar. +- DB-Migrationen additiv-first: neue Spalten/Tabellen hinzufügen, alte NICHT droppen, solange der neue Weg nicht durch Gate verifiziert ist. Drop-Migrationen erst in einem eigenen 'Cleanup-Release' nach C9. +- Feature-Flags wo machbar: dynamische Routes/Sidebar hinter einem Toggle (`DYNAMIC_PLUGIN_UI=true/false`), Default=false bis Gate C grün. Sofortiger Rollback = Flag off, kein Code-Revert. +- Deploy-Reihenfolge: Staging-Container (docker compose profile test) VOR Production. Production-Deploy nur mit grünem Gate. +- Vor jedem Block-Start: git tag `pre-block-` als Restore-Punkt. + +### F2 Was der Plan bewusst NICHT anfasst (Explosions-Schutz) +- Keine Schema-Änderungen an bestehenden 130 Tabellen (nur additive neue Felder: permission in Manifests ist Code, kein DB-Schema). +- Keine API-Path-Änderungen für bestehende Endpoints (Contacts-Entkopplung muss identische Pfade liefern — Endpoint-Diff-Test in B1 erzwingt das). +- Keine Auth-/Session-Logik-Änderungen (Security-Basics sind verifiziert gut — nicht anfassen). +- Keine gleichzeitige Änderung von Backend + Frontend im selben Commit (Backend erst, dann Frontend — C-Reihenfolge). + +### F3 Plugin-Development-Guide aktualisieren (VERHINDERT RÜCKFALL) ⚠️ Pflicht +**Befund:** docs/plugin-development-guide.md existiert (2549 Zeilen, gut strukturiert: Manifest, Lifecycle, UI-Registration, Events, Permissions, Migrations). ABER verifizierte Lücken: +- Contracts: nur 3 Erwähnungen — der zentrale Mechanismus der neuen Architektur fehlt fast komplett +- `depends_on`: 0 Erwähnungen — ARCH-026 führt es ein, der Guide kennt es nicht +- Kein 'Plugin-Checkliste'-Abschnitt, der die Fehlerklasse (Cross-Plugin-Imports, Lifecycle-Asymmetrie, fehlende Permissions) strukturell verhindert + +**Pflicht: Der Guide wird MIT jedem Block aktualisiert — ein Block gilt erst als done, wenn der Guide den neuen Zustand beschreibt.** + +| Nach Block | Guide-Update | +|---|---| +| A | Kapitel 'Contracts' vollständig: get_contract, register/unregister, Symmetrie-Pflicht activate/deactivate, register_event_handlers-Hook, EventBus-Duplikat-Regel | +| B | Kapitel 'Dependencies': depends_on im Manifest, topologische Aktivierung, Deaktivier-Schutz. Kapitel 'Entity-Registrierung': dynamische ENTITY_PERMISSIONS, Field-Contributions (statt CORE_FIELD_DEFINITIONS) | +| C | Kapitel 'Frontend-Contributions' aktualisieren: permission-Feld in FrontendPageRoute/FrontendMenuItem (PFLICHT, nicht optional), Widget-Registry statt statischem Loader, Workspace is_visible | +| D | Kapitel 'Konventionen': datetime.now(UTC) statt utcnow, Pydantic-Schemas statt dict-bodies, Audit-Log-Pflicht bei Mutationen | + +**Neuer Guide-Abschnitt 'Plugin-Checkliste' (Muss-Bestehen vor Merge jedes neuen Plugins):** +1. Kein direkter Import aus anderem Plugin — nur get_contract (Checker läuft in CI) +2. on_activate/on_deactivate symmetrisch: alles was registriert wird, wird auch deregistriert +3. Jede Route hat Permission (kein auth-only), jede Menü-Route hat permission-Feld im Manifest +4. depends_on deklariert, wenn Plugin andere nutzt +5. Entities über Plugin-Registrierung, keine Core-Annahmen +6. Frontend-Components über Manifest-Contribution, keine hardcoded Loader-Einträge +7. Migrationen folgen Namenskonvention, RLS für eigene Tabellen +8. Audit-Log bei allen Mutationen +9. datetime nur mit UTC +10. E2E-Smoke gegen Production-Build + +**Enforcement:** Die Checkliste wird als CI-Template-Check + Review-Checkliste referenziert. AGENTS.md §0 (Auf bestehendem Code aufbauen) bleibt die übergeordnete Regel. + +### Gate F +- Guide-Review: Jeder neue Mechanismus (Contracts, depends_on, permission-Felder, Widget-Registry) hat ein Kapitel mit Code-Beispiel aus einem REAL existierenden Plugin (nicht fiktiv). +- Checkliste als Datei (docs/plugin-checklist.md) existiert und ist in PR-Template verlinkt. +- Test: Ein Entwickler (oder Agent) baut ein Minimal-Plugin NUR mit dem Guide — ohne Fragen an den Architekten. Gelingt das nicht, ist der Guide nicht fertig. + +--- + +# BLOCK G — Compliance & Account-Security (NEU aus Final-Check 2026-08-23) + +Ziel: Rechtliche Pflichten (DSGVO) und Account-Sicherheit, die im Final-Check ans Licht kamen. Diese Punkte waren in v1, v2 UND dem ChatGPT-Review nicht enthalten. + +### G1 DSGVO-Compliance ⚠️ KRITISCH (verifiziert: grep 'gdpr' in app/routes + app/services = 0 Treffer) +Ein CRM mit Personendaten ohne DSGVO-Funktionen darf in DE nicht produktiv gehen: +- **Art. 15 Auskunft**: Endpunkt `GET /api/v1/me/data-export` — alle Daten einer natürlichen Person, maschinenlesbar (JSON). +- **Art. 17 Löschung**: Der von AGENTS.md geforderte Hard-Delete (`?gdpr=true`) existiert als Regel, aber KEIN Endpunkt implementiert ihn. Implementieren: anonymisiere/lösche Personendaten inkl. aller verknüpften Entities (Contacts, Addresses, Notes, Files, Mail-Verknüpfungen), respektiere Aufbewahrungspflichten (Buchhaltung), protokolliere die Löschung selbst im Audit. +- **Art. 20 Portabilität**: Strukturierter Export (JSON/CSV) der Kontaktdaten eines Tenants/Users. +- **Verarbeitungsübersicht**: doc (welche Daten, wo, wie lange) als Grundlage für den AVV. +- Aufwand: mittelgroßes Feature-Paket, eigener Track mit Tests; NICHT in die Architekturblöcke mischen. + +### G2 Session-Revocation bei Passwortänderung (verifiziert: nur Logout invalidiert) +- Befund: app/routes/auth.py:105 invalidiert NUR die eigene Session beim Logout. Eine Passwortänderung löscht NICHT andere aktive Sessions des Accounts. +- Risiko: kompromittierter Account bleibt nach PW-Wechsel kompromittiert (Angreifer-Session lebt weiter). +- Fix: Nach erfolgreichem Passwortwechsel/-reset alle Sessions des Users löschen (außer der aktuellen); optional E-Mail-Benachrichtigung über den Vorgang. +- Klein, wichtig, sofort machbar — gehört in Block 0 oder A. + +### G3 Hygiene-Funde (klein) +- dump.rdb im Repo-Root: git-ignored (kein Leak, verifiziert), aber aufräumen und Redis-Workdir sauber konfigurieren. +- scripts/test_migrations.sh:70 testet `alembic downgrade base`, ignoriert aber Fehler bewusst ('not always lossless'). Entscheidung dokumentieren: Downgrade-Fähigkeit ist KEIN Release-Kriterium — dann Kommentar so lassen und in docs/test-strategy.md festhalten. + +### Verifiziert GUT (nichts zu tun — explizit geprüft im Final-Check) +- Webhook-Signaturen: HMAC-SHA256 vorhanden (webhook_service.py:200-231) +- API-Token-Expiry-Prüfung vorhanden (api_token.py:91-95) +- Content-based MIME-Detection via python-magic (storage.py:32-34) +- CORS konfigurierbar über Settings (main.py:468-469) +- Redis: Passwort-Pflicht, Named Volume, Healthcheck (docker-compose.yaml:50-59) + +--- + +# BLOCK H — Agent-Plattform-Kern (NEU v3: Kernprodukt, war unsichtbar) + +⚠️ Höchste Priorität nach Block 0 — VOR bzw. zusammen mit Block B. + +Kontext: Die Plattform soll ERP-Fachmodule per Plugin nachrüsten und Agenten müssen die Software VOLL bedienen. Code-Verifizierung zeigt: Dieser Kernanspruch ist aktuell architektonisch NICHT erfüllt. + +### H1 Befunde (verifiziert 2026-08-23) +1. **Agent-Tools sind hardcoded**: `app/ai/integration_tools.py` registriert Workflow/Knowledge-Tools fest und importiert direkt aus Plugins (Core→Plugin, ARCH-011-Familie). Ein neues Fachmodul könnte seine Funktionen NICHT agenten-bedienbar machen, ohne diese Core-Datei zu ändern — direkter Verstoß gegen das Plattform-Prinzip. +2. **Agent-Laufzeit hängt an einem Plugin**: `ToolRegistry` liegt unter `app/plugins/builtins/ai_assistant/tool_registry.py`. Ist ai_assistant deaktiviert, existiert keine Registry mehr — Agent-Betrieb ohne dieses Plugin unmöglich. +3. **Manifest hat Contribution-Ansätze** (`agent_capabilities` manifest.py:200, `AgentDefinitionContribution.tool_ids` :88-94), aber keinen vollständigen Weg: BasePlugin kennt keinen register_tools-Hook; die Hardcodes umgehen das Manifest komplett. + +### H2 Architektur-Entscheidung (zuerst klären, dann bauen) +- **Agent-Laufzeit = Core**: `tool_registry`, `skill_registry`, `llm_client`, `agent_loop` gehören in `app/core/` bzw. `app/ai/` (Core-Layer), NICHT ins ai_assistant-Plugin. Das ai_assistant-Plugin wird zum normalen Fachplugin, das NUR seine eigenen Tools beiträgt. +- **Tool-Contribution-API**: Manifest-Feld `tools: list[ToolContribution]` (name, description, input_schema, handler_ref, required_permission) + BasePlugin-Lifecycle: on_activate registriert Tools beim Core-Registry, on_deactivate meldet sie ab (Symmetrie-Pflicht wie alle Contributions). +- **Permission-Gebundenheit**: Jedes Agent-Tool trägt die Permission des darunterliegenden Endpoints; der Agent kann nur handeln, was der ausführende User darf (bestehendes agent_permissions.py nutzen/konsolidieren). + +### H3 Umbau +1. Core-Registry nach app/core/ ziehen (Move + Alias-Import für Übergangszeit), ai_assistant nutzt sie wie jedes andere Plugin. +2. `integration_tools.py` auflösen: Die 4 Hardcoded-Tools werden zu regulären Contributions ihrer Quell-Plugins (workflows, knowledge, unified_search) via Manifest/Hook — Core-Datei verschwindet. +3. Plugin-Guide: neues Kapitel 'Agent-Tools beitragen' mit Schema + Beispiel + Test-Pflicht (Tool ohne Permission verboten). +4. ARCH-011-Core-Imports (ai/integration_tools, ai/agent_loop) lösen sich dadurch STRUKTURELL auf — nicht durch Contract-Surrogate an dieser Stelle. + +### Gate H +1. Neues Fachmodul-Testplugin bringt ein Agent-Tool MIT OHNE Änderung einer Core-Datei; Agent führt es aus (Tenant-scoped, permission-geprüft). +2. ai_assistant deaktivieren → Agent-Laufzeit läuft weiter; dessen eigene Tools sind weg, alles andere bleibt. +3. Deactivate/Activate-Zyklus: Tools sauber ab-/angemeldet, keine Duplikate (EventBus-Duplikatfix greift hier analog). +4. Jeder Tool-Call landet im Audit-Log mit actor=agent, acting_user, permission. + +### H4 Weitere Hardcoding-Systeme (gründlicher Nachscan 2026-08-23, alle code-verifiziert) + +Gleiche Fehlerklasse wie H1 — Systeme, die Fachmodule hardcodieren statt Contribution anzubieten: + +| ID | System | Ort | Befund | Fix-Richtung | +|---|---|---|---|---| +| HC-A | NL→Aktions-Mapping | app/ai/action_mapper.py:52-128 | map_query_to_actions() hat FESTE API-Pfade per Regex — neue Fachmodul-Endpunkte sind für Agenten-Vorschläge UNSICHTBAR | Aktions-Katalog aus Plugin-Manifest generieren (Endpoint-Registry existiert schon via OpenAPI) | +| HC-B | Workflow-Schritte | app/workflows/step_handlers.py:221,261,306,351,394 | Importiert 5 Contract-KLASSEN direkt (MailContract, CalendarContract, DmsContract, SearchContract, AutomationContract) statt get_contract() — neue Module können KEINE eigenen Step-Types beitragen | Step-Handler-Registry: Plugins registrieren Step-Types per Manifest; Core kennt nur Interface | +| HC-C | AI-Chat & Workflow-Notifications an kommunikation | app/ai/agent_loop.py:397 + app/workflows/engine.py:95 | CommConversation-Model direkt importiert — AI-Konversationspersistenz und Workflow-Benachrichtigung hängen am kommunikation-Plugin | Über kommunikation-Contract (besteht laut Checker-Output teilweise) oder Notification-Abstraktion | +| HC-D | Knowledge-Worker | app/core/worker.py:460 | KnowledgeExtraction-Model direkt importiert (deckt ARCH-040) | Contract oder generischer Extraction-Job über Plugin-Hook | +| HC-E | Compliance-Route | app/routes/compliance.py:22 | AgentDefinition-Model direkt importiert | Contract auf automation oder Model in Core-Schema überführen | + +**POSITIV-VORBILD im eigenen Code:** `unified_search/provider_registry.py` macht es RICHTIG — dynamisches register()/unregister()/get(), graph_rag contributet seinen Provider per Contract (graph_rag/plugin.py:44-58). Dieses Muster ist die Blaupause für ALLE H-Fixes. + +**Inkonsistenz gleich mitfixen:** knowledge/services.py:110 importiert provider_registry DIREKT statt den eigenen unified_search-Contract zu nutzen (Plugin→Plugin-Verstoß aus dem Vollscan). + +### H5 Frontend-Hardcodings (Nachscan Frontend 2026-08-23, code-verifiziert) + +| ID | System | Ort | Befund | Fix-Richtung | +|---|---|---|---|---| +| HC-F | Message-Block-Typen | frontend/src/components/comm/blocks/BlockRenderer.tsx:3-15,34+ | 14 Block-Typen hart im switch, darunter FACHMODUL-Blocks (TaskCardBlock, WorkflowCardBlock, KnowledgeCardBlock, ContactCardBlock, AgentResultBlock, ApprovalRequestBlock). Keine dynamische Registry (grep registerBlock/BLOCK_REGISTRY = leer). Ein ERP-Modul kann keine eigenen Message-Blocks beitragen | Block-Registry: Plugins registrieren Block-Component per Manifest-Contribution (component + block_type + permission); Core behält nur text/markdown/html/file/image/audio/video als Basis | +| HC-G | AISidebar-Tabs | frontend/src/components/layout/AISidebar.tsx:137-141 | 5 Tabs hardcodiert (chat/proactive/notifications/team/chatroom), Fachmodule können keine eigenen Sidebar-Tabs beitragen | Tab-Contribution über Manifest (wie Menü/Routes); Core behält Basis-Tabs, Plugin-Tabs kommen aus pluginStore | + +**Geprüft und sauber (Frontend):** Core-Layout-Komponenten (App.tsx, routes/, components/layout/, components/common/) importieren KEINE Plugin-API-Clients direkt. Die Doppelarchitektur-Probleme (Routes, Sidebar-Menü, Settings, Dashboard) sind bereits als C4–C7 geplant. + +**Gate H erweitert:** +5. Testplugin registriert einen eigenen Workflow-Step-Type OHNE Core-Änderung; Workflow führt ihn aus. +6. Agent-Query auf einen Fachmodul-Begriff schlägt dessen Aktionen vor (HC-A wirksam). +7. grep-Beweis: Keine der Dateien integration_tools/step_handlers/agent_loop/engine enthält noch Plugin-Model-Imports. +8. Testplugin contributet einen eigenen Message-Block-Typ; BlockRenderer rendert ihn dynamisch (HC-F wirksam). +9. Testplugin contributet einen AISidebar-Tab; Tab erscheint dynamisch (HC-G wirksam). + +--- + +# BLOCK E — Production-Härtung (NEU aus Deep-Dive 2026-08-23) + +Ziel: Der Unterschied zwischen 'Architektur repariert' und 'produktiv betreibbar'. Diese Punkte fehlten in v1 UND v2 komplett. + +### E1 Audit-Logging-Vollständigkeit +- Befund: Nur 1 Service-Datei referenziert AuditLog (grep-verified). AGENTS.md verbietet Mutationen OHNE Audit-Eintrag. +- Fix: Systematische Prüfung ALLER schreibenden Routes/Services auf Audit-Aufruf; Lücke schließen (Middleware-/Repository-Pattern statt handgestreut); Test: jede POST/PATCH/DELETE-Route erzeugt Audit-Zeile. + +### E2 E2E-Test-Realität +- Befund: BUG-012 — Playwright helpers.ts nutzt Mock-Daten. Die E2E-'Abdeckung' ist teilweise Schein. +- Fix: helpers.ts auf echte API umstellen; E2E gegen Production-Build + echte Backend-Instanz (docker compose profile test). Erst danach zählt E2E als Verifikations-Gate. + +### E3 Backup/Restore-Drill +- Vorhanden: backup.py, restore.py, restore_test.sh — aber ungeprüft, ob Restore auf FRISCHER DB wirklich reproduzierbar funktioniert. +- Fix: Automatisierten Restore-Drill in CI aufnehmen (Dump → leere DB → Restore → Smoke-Checks). Monatlicher manuell getriggerte Drill zusätzlich. + +### E4 Monitoring-Reality-Check +- Docs vorhanden (monitoring.md, incident-response-runbook.md) — tatsächliche Aktivierung ungeprüft. +- Fix: Health-Endpunkte extern erreichbar + Alerting konfiguriert? Metrics-Endpoint gefüllt? Einmal durchspielen und dokumentieren, was WIRKLICH alarmiert. + +### E5 Performance-Baseline +- Keine Lasttests erkennbar; bereits ein Search-Perf-Bug (6.34s) aufgetreten. +- Fix: Baseline-Messung der Top-10-Endpoints (p95), seed_perf_data.py nutzen, Schwellwerte dokumentieren. Kein Voll-Lasttest-Programm — nur Baseline + Regressionsschwelle. + +### E6 Secrets & Credential-Hygiene +- Befund: Sensible Credentials liegen lt. Projektanweisungen in docs/deploy-guide.md (privates Repo, aber Repo-Datei ≠ Secretstore). +- Fix: Triage wie D4; Ziel: Deploy-Guide referenziert Secretstore statt Werte zu enthalten. pip-audit/npm-audit-Routine in CI (aus S3 vorziehen: nur die AUTOMATISIERUNG, nicht die Altlasten). + +### E7 CI als hartes Gate +- Vorhanden aber ungeprüft: ci_pipeline.sh, .forgejo/workflows, .pre-commit-cross-plugin.yaml. +- Fix: Pipeline zum Pflicht-Gate machen: Import-Check + Cross-Plugin-Vollscan (nach 0.2-Fix) + ruff (85 Auto-Fixes zuerst bereinigen, dann Regel scharf) + tsc + pytest-Smoke. Merge ohne grün = unmöglich. + +### Reihenfolge von E +- E7 sofort nach Block 0 (CI braucht den funktionierenden Checker). +- E1 nach Block B (Audit braucht stabile Service-Schicht). +- E2–E6 parallel zu Block C/D, spätestens VOR Go-Live-Kandidat. + +--- + +# ARBEITSREGELN (aus Review abgeleitete Verbote/Gebote) + +1. ❌ KEINE globalen `commit()→flush()`-Transformationen. Nur nachgewiesene Einzelfälle mit Transaktionsbegründung (BUG-003-Muster). +2. ❌ KEIN pauschales `Base.metadata.create_all` in conftest. Unit-Fixtures ok; Integration/Install ausschließlich Alembic. +3. ✅ Erst Backend-Feld (`permission`), dann Frontend-Consumption. Nie umgekehrt. +4. ✅ Alte Wege (Routes/Sidebar/Settings/Dashboard-Hardcodes) erst nach Production-Build-Verify löschen. +5. ✅ Scanner-Zahlen sind Triage-Queues, keine Bugcounts. +6. ✅ Pro Task: Forgejo-Issue + PROGRESS.md-Update (AGENTS.md §9). Conventional Commits mit Finding-ID (`fix(arch-004): ...`). +7. ✅ Jede Änderung: minimal focused, bestehender Stil, Tests beweisen Wirkung. + +# AUSFÜHRUNGSREIHENFOLGE (kurz) + +``` +Block 0 (Sofort: SYNTAX-001, CHECKER-FIX) ~ halber Tag +Block A (Lifecycle/Contracts/Runtime) → Gate A +Block B (Plugin-Grenze, Contacts-Entkopplung) → Gate B (inkl. Fresh-DB) +Block C (Backend permission-Felder → Workspace → + Routes/Sidebar/Settings/Dashboard → Verify → Löschen) → Gate C +Block D (pytest-Massen, DateTime, API-Pfade, + Security-Triage, Marathon-Triage, Legacy) → Full Regression +Separate Tracks S1–S3: danach, unabhängig +``` diff --git a/docs/test-bugs.md b/docs/test-bugs.md index da1d899..b0d184d 100644 --- a/docs/test-bugs.md +++ b/docs/test-bugs.md @@ -801,19 +801,19 @@ Playwright E2E: 10 passed, 24 failed (BUG-011/012/013 — Mock-Daten und data-te - **Datei:** app/services/plugin_service.py:94, app/plugins/registry.py:612 - **Problem:** registry.activate() ruft on_activate() auf und setzt record.active=True BEVOR plugin_service.py Permissions registriert (Zeile 99-114). Bei Fehlern ist Plugin aktiv ohne Permissions. - **Schweregrad:** High -- **Status:** ⏳ Nicht gefixt +- **Status:** ✅ Gefixt 2026-08-23 (Block H/A) ### ARCH-002: Plugin on_activate() wird pro Tenant mehrfach aufgerufen - **Datei:** app/main.py:292-302 - **Problem:** `for tenant_id in all_tenant_ids: plugin.on_activate(plugin_db, container, event_bus)` — dieselbe Plugin-Instanz bekommt on_activate() pro Tenant. Event-Handler werden doppelt registriert. - **Schweregrad:** High -- **Status:** ⏳ Nicht gefixt +- **Status:** ✅ Gefixt 2026-08-23 (Block H/A) ### ARCH-003: /plugins/active-manifests hängt an plugins:read - **Datei:** app/routes/plugins.py:95 - **Problem:** Normaler User ohne plugins:read bekommt keine Plugin-Menüs/Routes. - **Schweregrad:** High -- **Status:** ⏳ Nicht gefixt +- **Status:** ✅ Gefixt 2026-08-23 (Block H/A) ### ARCH-004: Workspace/Sidebar is_visible nicht konsistent - **Datei:** frontend/src/store/workspaceStore.ts:100 @@ -855,7 +855,7 @@ Playwright E2E: 10 passed, 24 failed (BUG-011/012/013 — Mock-Daten und data-te - **Datei:** scripts/check_cross_plugin_imports.py:233 - **Problem:** `default=BUILTINS_DIR` — ohne --path wird nur app/plugins/builtins/ gescannt, nicht Core. - **Schweregrad:** Medium -- **Status:** ⏳ Nicht gefixt +- **Status:** ✅ Gefixt 2026-08-23 (Block H/A) ### ARCH-011: 27 Core→Plugin direkte Imports - **Dateien:** app/ai/ (8), app/core/ (7), app/routes/ (2), app/workflows/ (7), app/services/ (1), app/main.py (2) @@ -895,7 +895,7 @@ Playwright E2E: 10 passed, 24 failed (BUG-011/012/013 — Mock-Daten und data-te - **Datei:** app/plugins/builtins/contracts.py:88-89 - **Problem:** get_contract() macht _try_lazy_load() auch nach unregister(). Deaktivierte Plugins werden wieder sichtbar. - **Schweregrad:** High -- **Status:** ⏳ Nicht gefixt +- **Status:** ✅ Gefixt 2026-08-23 (Block H/A) ### ARCH-015: Notification-Type Lifecycle — Sync-Reihenfolge - **Datei:** app/plugins/registry.py:181-244,622-623 @@ -931,7 +931,7 @@ Playwright E2E: 10 passed, 24 failed (BUG-011/012/013 — Mock-Daten und data-te - **Datei:** app/core/event_bus.py:38 - **Problem:** `self._handlers[event_name].append(handler)` — gleicher Handler kann mehrfach registriert werden. - **Schweregrad:** High -- **Status:** ⏳ Nicht gefixt +- **Status:** ✅ Gefixt 2026-08-23 (Block H/A) ### ARCH-021: Sidebar.tsx statische UND dynamische Menüs - **Datei:** frontend/src/components/layout/Sidebar.tsx:54-60 @@ -985,19 +985,19 @@ Playwright E2E: 10 passed, 24 failed (BUG-011/012/013 — Mock-Daten und data-te - **Datei:** app/core/trigger_dispatcher.py:123-127 - **Problem:** `AutomationDefinition = automation_contract.Automation` (Zeile 123) wird ausgeführt BEVOR `if automation_contract is None` (Zeile 127). Wenn Contract None ist → AttributeError. - **Schweregrad:** High -- **Status:** ⏳ Nicht gefixt +- **Status:** ✅ Gefixt 2026-08-23 (Block H/A) ### ARCH-030: step_handlers.py — Contract.get_function() existiert nicht - **Datei:** app/workflows/step_handlers.py:221,261,306,351,394 - **Problem:** `MailContract.get_function("send_email")` — MailContract ist eine Klasse ohne get_function() Methode. Alle 5 Step-Handler Contracts haben dieses Problem. - **Schweregrad:** High -- **Status:** ⏳ Nicht gefixt +- **Status:** ✅ Gefixt 2026-08-23 (Block H/A) ### ARCH-031: knowledge/plugin.py — uuid nicht importiert (NameError zur Laufzeit) - **Datei:** app/plugins/builtins/knowledge/plugin.py:28,30 - **Problem:** `uuid.UUID(str(tenant_id))` und `uuid.UUID(str(article_id))` werden in `on_wiki_create` und `on_wiki_update` verwendet, aber `uuid` wird nie importiert. NameError bei erstem Wiki-Artikel-Ereignis. - **Schweregrad:** High -- **Status:** ⏳ Nicht gefixt +- **Status:** ✅ Gefixt 2026-08-23 (Block H/A) ### ARCH-032: knowledge/plugin.py + wiki/plugin.py — unregister_actions_by_owner mit falscher Signatur - **Dateien:** app/plugins/builtins/knowledge/plugin.py:66, app/plugins/builtins/wiki/plugin.py:41 @@ -1039,7 +1039,7 @@ Playwright E2E: 10 passed, 24 failed (BUG-011/012/013 — Mock-Daten und data-te - **Datei:** app/core/worker.py:168, app/plugins/base.py - **Problem:** Worker ruft `plugin.register_event_handlers(event_bus)` auf, aber BasePlugin hat keine solche Methode. `hasattr(plugin, 'register_event_handlers')` ist immer False. Im Worker werden Event-Handler NIE registriert — alle Events die über den Worker laufen (outbox events) werden nicht von Plugins verarbeitet. - **Schweregrad:** High -- **Status:** ⏳ Nicht gefixt +- **Status:** ✅ Gefixt 2026-08-23 (Block H/A) ### ARCH-039: worker.py — cleanup jobs verwenden datetime.utcnow() (naive datetime) - **Datei:** app/core/worker.py:cleanup_audit_log_job, cleanup_trash_job, cleanup_knowledge_job @@ -1051,13 +1051,13 @@ Playwright E2E: 10 passed, 24 failed (BUG-011/012/013 — Mock-Daten und data-te - **Datei:** app/core/worker.py:cleanup_knowledge_job - **Problem:** `from app.plugins.builtins.knowledge.models import KnowledgeExtraction` — Core importiert direkt von Plugin. Sollte über Contract laufen. - **Schweregrad:** Medium -- **Status:** ⏳ Nicht gefixt +- **Status:** ✅ Gefixt 2026-08-23 (Block H/A) ### ARCH-041: trigger_dispatcher.py — _dispatch_matching_agents hat None-Check nach Verwendung - **Datei:** app/core/trigger_dispatcher.py:172-173 - **Problem:** `AgentDefinition = automation_contract.AgentDefinition` (Zeile 172) wird ausgeführt BEVOR `if automation_contract is None` (Zeile 173). Wenn Contract None ist → AttributeError. Gleicher Bug wie ARCH-029. - **Schweregrad:** High -- **Status:** ⏳ Nicht gefixt +- **Status:** ✅ Gefixt 2026-08-23 (Block H/A) ### ARCH-042: calendar/contracts.py + dms/contracts.py — get_contract() erzeugt neue Instanz statt registrierte zu nutzen - **Dateien:** app/plugins/builtins/calendar/contracts.py:get_contract, app/plugins/builtins/dms/contracts.py:get_contract @@ -1087,13 +1087,13 @@ Playwright E2E: 10 passed, 24 failed (BUG-011/012/013 — Mock-Daten und data-te - **Datei:** app/routes/compliance.py:22 - **Problem:** `from app.plugins.builtins.automation.models import AgentDefinition` — Core Route importiert direkt von Plugin Model. Sollte über Contract laufen. - **Schweregrad:** Medium -- **Status:** ⏳ Nicht gefixt +- **Status:** ✅ Gefixt 2026-08-23 (Block H/A) ### ARCH-047: step_handlers.py — SearchContract statt UnifiedSearchContract - **Datei:** app/workflows/step_handlers.py:306 - **Problem:** `from app.plugins.builtins.unified_search.contracts import SearchContract` — die Klasse heißt `UnifiedSearchContract`, nicht `SearchContract`. ImportError zur Laufzeit. - **Schweregrad:** High -- **Status:** ⏳ Nicht gefixt +- **Status:** ✅ Gefixt 2026-08-23 (Block H/A) ### ARCH-048: engine.py — register_workflow_event_handlers nutzt falschen payload key - **Datei:** app/workflows/engine.py:register_workflow_event_handlers @@ -1105,7 +1105,7 @@ Playwright E2E: 10 passed, 24 failed (BUG-011/012/013 — Mock-Daten und data-te - **Datei:** app/workflows/engine.py:94,95 - **Problem:** `from app.plugins.builtins.kommunikation.models import CommConversation` — Core importiert direkt von Plugin. Sollte über Contract laufen. - **Schweregrad:** Medium -- **Status:** ⏳ Nicht gefixt +- **Status:** ✅ Gefixt 2026-08-23 (Block H/A) ### ARCH-050: engine.py — acquire_lock awaitet nicht-async get_redis() - **Datei:** app/workflows/engine.py:acquire_lock @@ -1135,7 +1135,7 @@ Playwright E2E: 10 passed, 24 failed (BUG-011/012/013 — Mock-Daten und data-te - **Datei:** app/routes/entity_permissions.py:259 - **Problem:** `model_info = ENTITY_MODELS.get(entity_type)` gibt eine Model-Klasse zurück, nicht ein dict. `model_info["model"]` wirft TypeError. Sollte `model = ENTITY_MODELS.get(entity_type)` sein. - **Schweregrad:** High -- **Status:** ⏳ Nicht gefixt +- **Status:** ✅ Gefixt 2026-08-23 (Block H/A) ### ARCH-055: errors.py — error.userAgent statt error.user_agent - **Datei:** app/routes/errors.py:124