Files
leocrm/PROGRESS.md
T

642 lines
78 KiB
Markdown
Raw Normal View History

# LeoPlatform — Fortschritts-Tracking
## Weitermachen (2026-08-28, für das nächste Modell)
**Produktion läuft stabil auf `20ff5e2`** (Health healthy, Alembic 0142, RLS 109). Alle Forgejo-Issues #351#358 geschlossen.
**Offene Pakete (nach Größe, kleinste zuerst):**
1. **Saved-Filters 422-Test** — ✅ erledigt (d7b3c7c... der Fix wurde 2026-08-28 in `0d052ab` deployed)
2. **AppShell ×4 + Router ×2 Mock-Fixes** — ✅ erledigt (2026-08-28): AppShell war bereits grün (useCurrentUser-Mock existierte in Zeile 9; Plan-Eintrag veraltet, bewiesen: Lauf 23:00 'Tests 2 failed (6)' = nur Router). Router.test.tsx gefixt: QueryClientProvider + Mocks useCurrentUser/useUserPermissions → **2/2 passed (2.03s)**
3. **ContactEditModal File-Level** — ✅ erledigt (2026-08-28): Geister-Test nach §10 gelöscht. Ursache bewiesen: ContactEditModal.tsx (349 Z.) wurde in db4701b (BUG-080/082) gelöscht, Test blieb → Vitest 'Failed to resolve import' (1 failed, no tests). Ersatz ContactEditForm.tsx lebt und wird von ContactsList/ContactDetailPage genutzt
4. **custom_field_definitions generisch machen** — ✅ erledigt (2026-08-29): W4b-Muster komplett angewendet. Route: 422-Entity-Validierung + 403-Owner-Modul-Check (contacts:read etc.) auf create/list/update/delete, ACL-Loch gefixt (delete übergab user_id nie → 500 für alle Nicht-Admins), PermissionError→403-Handler. Shape-Fix: Backend lieferte Array, alle 6 Frontend-Consumer lesen `data.items` → CustomFields-UI zeigte seit jeher leer; jetzt `{items,total}` (CustomFieldDefinitionListResponse). entity_permission_service: Plural-Ableitungs-Fix ('workflow'→workflows:read statt Phantom contacts:read; 'address'→addresses:read via +es), unregister_entity_model räumt ENTITY_PLUGIN_OWNERS mit auf (Lifecycle-Leak), zentrale Helper validate_entity_type/check_entity_read_permission — saved_filters+saved_views Duplikate entfernt (Aliase, Call-Sites unverändert). Neue Suite tests/test_custom_field_definitions.py **13 Tests**: Rot bewiesen 10 failed/4 passed → Grün **25 passed** (13 cfd + 12 saved_filters-Regression), Permission-Suiten 22/22, custom_fields+lifecycle 13/13, ruff exit=0, create_app OK. 3er-Kombi-Failures (custom_fields+entity_registry+lifecycle) per Stash als identischer Vorbestand bewiesen (clean HEAD: gleiche 7 Failures — Suite-Isolation, kein Zusammenhang mit Änderung). api-documentation.md ergänzt (4 Endpoints)
5. **Sidebar /contacts statische Route entfernen** — ✅ erledigt (2026-08-29): Kritikpunkt 21 zuerst erfüllt (Renderer bewiesen, DANN entfernt). PluginRouteRenderer komplett neu: verschachtelte `<Routes>` statt manueller find()-Logik — vorher bewiesen kaputt: ':id'-Patterns konnten NIE matchen (Detail-Deep-Link /contacts/abc hätte die Liste gerendert), '/contacts/dedup' wäre auf die Liste gefallen, useParams() blieb im catch-all leer. Rot 4 failed → **Grün 9/9 Tests** (inkl. :id-Match, useParams={"id":"abc-123"}-Beweis, dedup-schlägt-:id-Spezifität). STATIC_COMPONENT_MAP um 3 Contacts-Seiten ergänzt (ARCH-019: Production-Build kann Runtime-Fallback nicht laden; Named-Exports explizit, Pages haben kein default). Statische Routen /contacts, /contacts/:id, /contacts/dedup aus index.tsx entfernt + 3 ungenutzte lazy-Imports; /trash (contacts:read) und /guest/contacts unberührt. Gates: tsc OK · **Production-Build BUILD_EXIT=0 (2.76s) mit frischen Chunks ContactsList-DZ-WOyL2.js/ContactDetailPage-LZBUypcB.js/DedupMerge-H6U7AYKS.js (02:04)** · Renderer 9/9 · routePermissions 6/6 · Router 2/2 · Sidebar-Nav-Quelle bewiesen: Sidebar.tsx Z.97 flatMap menu_items (Manifest, unberührt). AppShell-Solo/Combo-Worker-Crash = dokumentierter Vorbestand (heute 3× vor Änderung reproduziert)
6. **Kontakt-Model ins ContactsPlugin** — ✅ erledigt (2026-08-29): Contact/ContactPerson (258 Z.) physisch nach `app/plugins/builtins/contacts/models.py` (Mail-Vorbild). `app/models/contact.py` = PEP-562-Lazy-Re-Export-Brücke: alle 35 Import-Stellen (8 Core + 7 Plugin + 19 Tests + env.py) unverändert lauffähig; models/__init__.py Contact-Import lazy via Package-__getattr__. Rot-Lauf entlarvte ECHTEN Zirkel (conftest→core.auth→models→Shim→plugins→registry→cache→core.auth teilweise initialisiert → ImportError) — mit Lazy-__getattr__ bewiesen behoben, jedes Einstiegsschema zirkelfrei. TDD: Rot 6 failed → **Grün 9/9** (neue Suite test_contacts_model_ownership.py: Ownership, Shim-Identität, Alembic-Metadata-Integrität, Sync-Exclude, FK-Kette). sync_plugin_schema.py: ALEMBIC_OWNED_TABLES={contacts,contactpersons} verhindert Dual-Ownership (142 Alembic-Migrationen besitzen das Schema). Checker: Shim in EXEMPT_PATHS (dokumentierte Brücke), **0 Verstöße/483 Dateien**. Gates: create_app OK · ruff exit=0 · Regressionen: entity_registry 3/3, lifecycle 2/2, custom_fields 11/11, **auth solo 11/11** (Kombi-Failures = per Solo-Lauf bewiesener Isolations-Vorbestand) · **alembic upgrade head auf frischer DB OK** (0105 contacts_tsv_trigger recreated). Keine neue Migration, migration_hashes unberührt
**Wichtig:** AGENTS.md-Regeln zuerst lesen (§0.0 Sub-Agents nur für einfache Jobs, §0.2 auf bestehendem Code aufbauen, §10 'PROGRESS.md als Source of Truth').
> **Letztes Update:** 2026-08-29
## Produktions-Bugfixes (2026-08-27)
| Bug | Issue | Fix | Verifikation (Live-Messung 2026-08-27) |
|---|---|---|---|
| KI-Chat `Stream failed: 403` (sessionStorage-Key `leocrm_csrf_token` wird nie geschrieben → Request ohne X-CSRF-Token) | [#351](https://forgejo.media-on.de/Leopoldadmin/leocrm/issues/351) | streamChat nutzt `getCsrfToken()` aus dem gemeinsamen Client | Vitest streamChat.test.ts 2/2 passed: X-CSRF-Token-Header bewiesen |
| Alle Mutationen (Wiki-Save etc.) 403 nach Seiten-Reload (`/auth/me` lieferte csrf_token nicht zurück → In-Memory-Token nach Reload weg) | [#351](https://forgejo.media-on.de/Leopoldadmin/leocrm/issues/351) | `/auth/me` liefert `csrf_token` aus Session; useCurrentUser stellt ihn beim Bootstrap wieder her | pytest test_auth.py 11/11 passed inkl. neuem Regressionstest `test_me_returns_csrf_token_for_reload_restore` |
**Gates:** ruff exit=0 · tsc --noEmit exit=0 · pytest 11 passed · Vitest 2 passed
## W4c — Custom-Fields-Routen in ContactsPlugin migriert (2026-08-28) ✅
**Verify-first:** `app/routes/custom_fields.py` war 100% Contact-spezifisch (importiert Contact, nutzt contacts:read/write, Route /{contact_id}/custom-fields) — lag aber als scheinbar generischer Core-Service (Kritikpunkt 14).
**Fix (c6decf5):** Die komplette Logik (2 Endpoints GET/PATCH, `_collect_custom_field_definitions`, `_merge_definitions_with_values`, `CustomFieldUpdateRequest`) wandert in `app/plugins/builtins/contacts/routes.py` (gleicher Router-Prefix /api/v1/contacts, bereits via manifest.routes gemounted). `app/routes/custom_fields.py` gelöscht, main.py bereinigt. Der generische `custom_field_definitions.py`-Endpoint bleibt im Core.
**Verifikation:** tests/test_custom_fields.py **11/11 passed** (Funktionserhalt) · create_app OK · ruff grün · Full Deploy SUCCESS · Health healthy
## Phase L1-L3 — Dokumente-Generator Backend+Editor (2026-08-29) ✅
**Scope:** Briefpapier (letterheads) + Druckvorlagen (print_templates) + Assets (document_assets) + Block-Registry + Contract-Beiträge + Drag&Drop-Editor + globaler Dokument-Dialog. Erweiterung des report_generator-Plugins (kein Neubau).
**Umgesetzt:**
- Backend: `documents.py` (13 Endpoints), `document_blocks.py` (Registry: text/image/shape/table/spacer/divider/placeholder/pagebreak + Modul-Beiträge via `document_blocks()`-Contract), `document_renderer.py` (Blocks→HTML→PDF, WeasyPrint data:-URI-only SSRF-Policy, Briefpapier-@page-Frame mit running header/footer)
- Contract-Beitrag contacts: `document_placeholders(entity_type)`, `document_data(db, tenant_id, entity_id, entity_type)` (#359-Muster wie importexport_entities)
- Migration: Plugin-SQL 0003 (idempotent) + Alembic 0143 (Dual-Path-Konvergenz, RLS fail-closed nach 0084-Muster)
- Frontend: `api/documents.ts` + `DocumentSettings`-Page (Settings→Dokumente, eigener Menüpunkt via settings_pages) + `BlockEditor` (@dnd-kit: Palette/Canvas/Config-Panel/Live-Preview-iframe) + `LetterheadEditor` + `PrintTemplateEditor` + `DocumentGenerationDialog` (global für Module, integriert in ContactDetailPage)
- i18n de/en vollständig
**Verifiziert:**
- ✅ tests/test_documents_generator.py: 32/32 (CRUD, Tenant-Isolation, RBAC 403, Block-Validierung 422, Preview, Render-PDF `%PDF`, Assets, Contract-Unit)
- ✅ Regression: test_report_generator.py + test_plugin_route_order.py 9/9
- ✅ tsc --noEmit Exit 0; Production-Build OK (2.79s)
- ✅ Alembic-Fresh-DB: 0001→0143 komplett, letterheads/print_templates/document_assets mit RLS+FORCE+crm_api-Policy bewiesen (Scratch-DB wieder gedroppt)
- ✅ ruff check clean; check_migration_hashes 93/93 OK
- ⚠️ Bekannt: Router-Reihenfolge im Manifest — documents-Router muss VOR routes stehen (/{report_id}-Catch-all)
**Offen (Folgepakete):**
- L4: KI-Steuerung („Erstelle Rechnungsvorlage") via agent_loop
- L5: E-Rechnung XRechnung/ZUGFeRD (benötigt Verkaufs-Modul)
- Weitere Module können Blöcke/Platzhalter beisteuern (Contract-Muster dokumentiert in plugin-development-guide.md)
## Phase L4-L5 — KI-Vorschlag + XRechnung-Format-Layer (2026-08-29) ✅
**User-Klärung:** Verkaufsmodul kommt später — aber das XRechnung-FORMAT ist jetzt implementiert (reiner Format-Layer, kein Rechnungs-CRUD).
**Umgesetzt:**
- L5 Format-Layer: `einvoice.py` — EN16931/XRechnung CII-XML-Generator (ElementTree, XML-Escaping gratis), Pflichtfeld-Validierung mit BT/BG-Codes (BT-1/2/3/5, BT-10, BT-27, BT-31/32, BG-25, BT-126/146), Decimal-kommerzielles Rounding, Header-Tax-Breakdown pro VAT-Satz, Profile en16931|xrechnung (Guideline urn:xoev-de:kosit:standard:xrechnung_3.0)
- Endpoints: `/einvoice/render` (inline → XML), `/einvoice/validate` (422 mit Fehlliste), `/einvoice/render-for` (Contract-Resolver `einvoice_data()` — Andockpunkt Verkaufsmodul, ohne Beitrag 404 no_data_source)
- L4 KI-Steuerung: `/documents/suggest` — natürliche Sprache → Block-Komposition via zentralem llm_complete (gpt-4o-mini, Cost-Tracking, Tenant-Budget), Registry-Sanitizing (ungültige KI-Blöcke gefiltert, IDs serverseitig), Code-Fence-Stripping, 502 ai_unavailable/invalid_ai_response
- Frontend: KI-Vorschlag-Panel im PrintTemplateEditor (Sparkles, Prompt-Textarea, Vorschläge werden an Blöcke angehängt), i18n de/en
**Verifiziert:**
- ✅ TDD: Rot 25 failed → ✅ Grün **25/25** (tests/test_einvoice_generator.py: Validierung 6 Unit, XML-Struktur 5 Unit inkl. Escaping/Profil/Summen, Contract-Resolution 2 mit Mock-Registry, API 6: 200-XML/422-BT-Codes/403/404, Suggest 6: Mock-LLM/Filter/Fence/502/403)
- ✅ tsc exit 0 (useMutation-Typisierung SuggestResult,Error,SuggestInput), Production-Build BUILD_EXIT=0
- ✅ ruff clean
**Offen:** Verkaufsmodul dockt später via `einvoice_data()` an — Contract + Doku (plugin-development-guide.md) fertig.
## W3b — Settings Contribution-Wahrheit (2026-08-28) ✅
**Verify-first (Live-Messung):** 7 Plugins liefern `settings_pages` via Manifest (mail, ai_assistant, ai_proactive, automation, permissions ×3, system_notif) — die hardcoded Items in `Settings.tsx` für mail/ai/notifications waren identische Duplikate.
**Fix (b1a7551):** hardcodedNavItems auf 7 echte Core-Settings reduziert (stammdaten, user-management, system, custom-fields, webhooks, workspaces, backup) — Plugin-Settings kommen ausschließlich via pluginNavItems. Path-Dedup bleibt als Sicherheitsnetz.
**Dashboard-Verify (Kritikpunkt 20a):** Dashboard.tsx lädt Widgets bereits dynamisch via `useDashboardWidgets()` API (Manifest-Contributions von calendar/contacts/tasks) → DashboardWidgetLoader ist nur der Vite-Code-Splitting-Renderer, **keine fachliche Doppelquelle** — Kritikpunkt 20a teilweise widerlegt.
**Verifikation:** Vitest 90/90 (13 Dateien inkl. settings + dialog + permissions) · tsc exit 0 · frontend-only Deploy FE_EXIT=0
## Suite-Isolation (2026-08-28) ✅
**Mechanismus (Live-Messung):** `close_engine()` in der rbac `mail_app`-Fixture disposiert UND setzt alle globalen Engines auf None — 12 ACL-Batch-Failures (`relation "users" does not exist`) in Nachfolger-Suiten.
**Fix (b691dd3):** `reset_engine_for_testing(engine)` nach `close_engine()` im Teardown — conftest-Engine wird als globale Engine wiederhergestellt (Produktions-Bootstrap-Spiegelung).
**Verifikation:** ACL-Batch vorher 12 failed/118 passed → nachher **130 passed** (alle 12 behoben).
**Nur tests/ geändert — kein Production-Deploy nötig.**
## W4a — Import/Export Contribution-Architektur (2026-08-27, SPECS final)
**Spec:** [#359](https://forgejo.media-on.de/Leopoldadmin/leocrm/issues/359) (user-abgestimmt)
**Kernentscheidungen:** Zentraler Dialog (Modal lg/xl) geöffnet per Toolbar-Button in jeder Modulliste, deren Plugin Import/Export anbietet; /import-export-Seite wird zur Übersicht aller Angebote (Variante b). Formate als Plugins (CSV/JSON/XLSX bundled; PDF später separat). Module = Contributors via Contract (`importexport_meta`); Core = Orchestrator + Sicherheits-Policy-Layer (Sensitive-Filter, Tenant-Scoping, Audit unabhängig vom Modul erzwungen). Modul-native Formate (ICS/EML/ZIP) registrieren sich nur anzeigend.
**Abgelöst:** `export_service.py` (78 Z., CSV-only, alter /export-Endpoint) + contact-spezifische Logik in `import_export_service.py` (504 Z.) — der bewiesene Doppel-Weg wird konsolidiert.
### Phase 1 — Backend-Kern ✅ (cd8ef75, deployed 21:14)
- Format-Registry `app/core/importexport_registry.py` (FormatHandler-Protokoll, `available_for()` Schnittmenge)
- `importexport_formats`-Plugin (csv/json/xlsx), lifecycle-korrekt (on_activate registriert, on_deactivate entfernt)
- ContactsContract `importexport_entities()` + `ie_*`-Methoden (Columns, Validatoren, Normalizer, Fetch, Persist mit Audit)
- `import_export_service.py` generische Engine — iteriert über `registry.list_discovered()`, keine hartcodierten Plugin-Namen
- **Funktionserhalt: 45/45 import_export-Suite passed** (inkl. Fehler-Multiplizität: 2 failed rows → 3 total_errors via ie_required + ie_row_valid-Trennung; Zwischenstand mit 2 Failures live gefangen und korrigiert)
### Phase 2 — Frontend-Dialog ✅ (38df597, deployed 21:34)
- `ImportExportDialog.tsx` (neu): Modal lg/xl, Export-Tab (1 Schritt) + Import-Tab (4 Schritte: Datei → Mapping-Editor → Dry-Run → Ausführung+Report inkl. Background-Job-Polling)
- 24 `importexport.*`-i18n-Keys (de + en, keine hardcoded Strings)
- ContactsList: Toolbar-Button (contacts:read-Gate, Upload-Icon, entityType vorgewählt) über pluginToolbarStore
- **Verifikation: Vitest 12/12** (importExportDialog 6/6 + routePermissions 6/6) · tsc exit 0 · Production-Build exit 0 · 6 contacts/shell-Failures per Stash-Test als Vorbestand bewiesen (identisch auf clean HEAD) · frontend-only Deploy FE_EXIT=0
### Phase 3 — PDF (separat, offen)
PDF als weiteres Format-Plugin — eigener Design-Baustein (Library-Choice, Templates pro Modul).
## Welle 3a — Route-Permission-Wahrheit (2026-08-27)
| Finding | Issue | Fix | Verifikation (Live-Messung) |
|---|---|---|---|
| **Phantom-Permission:** `/communication` prüfte `communication:read` — nirgends registriert (Plugin liefert nur `comm:*`) → Nicht-Admins mit gültigem comm:read bekamen die Seite nie | [#358](https://forgejo.media-on.de/Leopoldadmin/leocrm/issues/358) | → `comm:read` (Backend+Manifest-Wahrheit) | Vitest routePermissions.test.ts **6/6 passed** (Source-Inspektion: 5 Route-Korrekturen + Phantom-Nachweis) |
| `/mail/settings` prüfte zu schwaches `mail:read` — Backend verlangt `mail:config` | [#358](https://forgejo.media-on.de/Leopoldadmin/leocrm/issues/358) | → `mail:config` | dito |
| `/import-export` prüfte `contacts:read` — Backend (Core-Modul `app/routes/import_export.py`) verlangt `import_export:read`; Kritik-Aussage „import_export-Plugin" widerlegt (Existenz geprüft: keins) | [#358](https://forgejo.media-on.de/Leopoldadmin/leocrm/issues/358) | → `import_export:read` (in CORE_PERMISSIONS registriert, Zeile 59) | dito |
| `/activity` prüfte Phantom `activity:read` (nirgends registriert, kein Backend-Nutzer); Seite nutzt Audit-API | [#358](https://forgejo.media-on.de/Leopoldadmin/leocrm/issues/358) | → `audit:read` | dito |
| `/wiki` ungeschützt im statischen Router — Backend verlangt `wiki:read` | [#358](https://forgejo.media-on.de/Leopoldadmin/leocrm/issues/358) | → `wiki:read` | dito |
**Gates (Kritikpunkt 21 erfüllt):** Vitest 6/6 · tsc --noEmit exit 0 · **Production-Build exit 0 (vor Commit)** · frontend-only Deploy FE_EXIT=0
## Welle 2b — Contacts-Entity-Registry Single-Source (2026-08-27)
| Finding | Issue | Fix | Verifikation (Live-Messung) |
|---|---|---|---|
| Doppelquelle: statische `contact/contacts/company`-Einträge in `ENTITY_MODELS` neben identischer dynamischer Lieferung via `ContactsPlugin.get_entity_models()` (Kritikpunkte 911) | [#357](https://forgejo.media-on.de/Leopoldadmin/leocrm/issues/357) | 3 statische Einträge entfernt — ContactsPlugin ist Single Source; conftest spiegelt Produktions-Bootstrap idempotent (autouse-Fixture); `contact_folder` etc. bleiben korrekt (echte Core-Entities, von keinem Plugin geliefert) | Regressionstests `tests/test_contacts_entity_registry.py` **3/3 passed** (Source-Inspektion + Plugin-Lieferung + Bootstrap); entity_permissions + cross_tenant_security Suiten grün; ruff 0 Fehler; create_app OK |
| VORBESTAND bewiesen: 12 ACL-Batch-Failures durch Suite-Isolation (`relation "users" does not exist` in Nachfolger-Suiten) | [#357](https://forgejo.media-on.de/Leopoldadmin/leocrm/issues/357) (Body) | Kein Fix in diesem Commit — Stash-Test: identische 12 Failures auf clean HEAD (118 passed) vs. mit Fix (121 passed, nur +3 neue Tests) | Separates Isolation-Bugfix-Paket nötig |
**Gates:** ruff modified-files 0 · pytest entity_registry 3/3 + ACL-Funktionserhalt grün · create_app OK
## Welle 2 — Cross-Plugin/DSAR-Fix (2026-08-27)
| Finding | Issue | Fix | Verifikation (Live-Messung) |
|---|---|---|---|
| 4 Core→Plugin-Imports in `core/jobs.py` DSAR-Sammlung | [#356](https://forgejo.media-on.de/Leopoldadmin/leocrm/issues/356) | Schritt 1 (092c2d2): Imports auf `get_contract()` umgestellt | Checker 4→0; DSAR-Suite 4/4 |
| **Vertiefung nach Review-Einspruch:** Plugin-Fachlogik (welche Kategorien, welche Felder, Limits) lag weiterhin hart im Core — Core kannte Plugin-Details | [#356](https://forgejo.media-on.de/Leopoldadmin/leocrm/issues/356) | **Komplette Extraktion:** `dsar_collect()`/`dsar_erase()` in die 5 beteiligten Contracts (contacts, mail, tasks, calendar, kommunikation); `core/jobs.py` sammelt/löscht nur Core-eigene Daten (Profil, Audit, Notifications, User-Anonymisierung) und iteriert generisch über `registry.list_discovered()` → Contract-DSAR-Beiträge. **Neue Plugins liefern DSAR-Kategorien ohne Core-Änderung.** | Checker **0 Verstöße**; DSAR-Suite **4/4 passed** (Counts-/Category-Keys unverändert: `contacts`, `contacts_soft_deleted` etc. via Contracts); `create_app()` OK; ruff nur 2 Vorbestand-N811; korruptes tasks/contracts.py (Patch-Artefakt, ast-gefangen) sauber neu geschrieben |
| P16 manuell klassifiziert: `app.models.contact`-Imports in core/jobs.py + worker.py sind KEINE Verstöße (Contact liegt im Core-Models-Layer) | — | Keine Aktion nötig, dokumentiert in #356 | Checker-Regex deckt nur `app.plugins.*` ab — korrekt so |
**Gates:** ruff modified-files grün (2 Vorbestand N811 ausgenommen) · Cross-Plugin-Checker 0 · pytest DSAR 4/4 · create_app OK
## Welle 1 — Plugin-Lifecycle-Fix (2026-08-27)
| Finding | Issue | Fix | Verifikation (Live-Messung) |
|---|---|---|---|
| P1: `was_already_active`/`was_already_inactive` wurden in `plugin_service.activate/deactivate_plugin()` aus dem Record NACH dem Registry-Aufruf berechnet → konstant falsch → Runtime-Deregistrierung beim Deactivate war toter Code; Activate-Zweig lief nie | [#355](https://forgejo.media-on.de/Leopoldadmin/leocrm/issues/355) | Vorher-Status wird VOR dem Registry-Aufruf gelesen (`_get_plugin_record`) und beide Zweige laufen jetzt wirklich | TDD: neuer echter Integrationstest `tests/test_plugin_lifecycle_service.py` (install→activate×2→deactivate×2→re-activate über PluginService, beweist Permissions×1, Gate-Eintrag, ENTITY_MODELS on/off) rot→grün; Regression 93 passed (nur bekannter #354-Vorbestand) |
| P3: `registry.activate()` synced Notification Types VOR dem Statusupdate → Types des frisch aktivierten Plugins fehlten | [#355](https://forgejo.media-on.de/Leopoldadmin/leocrm/issues/355) | `sync_notification_types()` hinter DB-Statusupdate+Flush verschoben | Beweis im selben Integrationstest: NotificationType existiert nach activate, entfernt nach deactivate |
**Gates:** ruff modified-files grün · pytest Lifecycle 2/2 + Regression 93 passed · Deploy folgt
## Legacy-Cleanup (2026-08-27)
| Aktion | Commit | Verifikation (Live-Messung) |
|---|---|---|
| Toter AI-Copilot-Legacy gelöscht (`ai_copilot.py` Routes+Service+Schema+Model+Geister-Test, schemas/__init__, OpenAPI-Tag) — Chat läuft seit Migration 0137 über kommunikation/comm_conversations | b50a933 | Router war nie gemountet; Prod-DB: `relation "ai_conversations" does not exist`; create_app OK 83 routes identisch auf clean HEAD (Stash-Beweis); ruff modified-files grün; pytest auth/plugins/marketplace 100 passed |
| Vorbestand-Failure dokumentiert: `test_contacts_lifecycle ..._without_contacts_special_case` erwartet >100 Backend-Routen, Architektur liefert 83 (Routen in Manifesten/Frontend-Router) | [#354](https://forgejo.media-on.de/Leopoldadmin/leocrm/issues/354) | Stash-Test auf ebf4b03: identischer Failure → Vorbestand bewiesen |
Bekannter Rest (aus 27-Punkte-Kritik gegengeprüft): Plugin-Lifecycle-Cleanup tot (P1), sync_notification_types-Reihenfolge (P3), manueller Lifecycle-Test (P2), Cross-Plugin 4 Verstöße core/jobs.py + versteckte app.models.contact-Imports (P15/P16), Sidebar-Sonderdrahtung contacts (P10), statische ENTITY_MODELS (P11), saved_views/filters contacts:read-Pin (P13), Settings-Dedupe (P19), Dashboard hartes Widget-Map (P20a). Welle 1 (Lifecycle+echter Integrationstest) startet als nächstes.
> **Audit:** Komplette Vernetzungs-Audit durchgeführt — ~1800 Vernetzungen, 93% verbunden, 6 kritische Findings
---
## 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 |
| ARCH-043 | automation Tenant.limit(1) statt System-Tenant | ✅ get_system_tenant() + system_tenant_slug Setting | 17516d2 |
| ARCH-052 | storage get_file_metadata Event-Loop im async Kontext | ✅ get_file_metadata_async() + Fail-Fast-Guard | 17516d2 |
| ARCH-008 | Permission-Namensschema inkonsistent | ✅ Kanon modul:aktion festgelegt, Manifest-Validator erzwingt es | 7953077 |
| ARCH-009 | Tote 3-Segment-Rollen-Patterns (core:*:X) + 14 Route-Literals | ✅ Migration 0141 + Route-Fix, Roundtrip bewiesen | 7953077 |
| ARCH-012 | wiki/knowledge on_deactivate unvollständig | ✅ Provider-Dereg + 2 latente Bugs (register_provider fehlte am Contract, kaputter Modul-Import) behoben | c21634b |
| ARCH-013 | self_improvement Fallback-Import; Benachrichtigung war tot | ✅ Contract-only; undefinierten KommunikationContract-Verweis behoben | c21634b |
| ARCH-015 | Notification-Sync fehlt in Deactivate-Sequenz | ✅ sync_notification_types nach Status-Update | c21634b |
| ARCH-033 | comm_websocket/comm_miniapps bleiben im Container | ✅ Container-Cleanup VOR super(); ServiceContainer.remove() ergänzt | c21634b |
| ARCH-034/035 | self_improvement/marketplace Contract-Unregister | ✅ verifizierte No-Ops: beide registrieren keinen Contract | c21634b |
| ARCH-036 | mail _auto_sync_task Klassenvariable | ✅ Instanzvariable via __init__ | c21634b |
| ARCH-037 | graph_rag Registrierung VOR super() | ✅ Reihenfolge umgestellt | c21634b |
| ARCH-044 | ai_ui_control remove() NACH super() | ✅ Reihenfolge umgestellt; fehlendes ServiceContainer.remove() ergänzt | c21634b |
| Gate A | Block-A-Abschlussprüfung | ✅ BESTANDEN (4/4): Imports, Lifecycle-Symmetrie, Activate-Once, Contract-Roundtrip | 32f63ad |
| B1 | Contacts-Domain aus Core entkoppelt: 4 Router ins Plugin verschoben, manifest.routes mit require_active_plugin-Schutz | ✅ Endpoint-Diff 409/0/0/0 identisch; Acceptance-grep fachfrei; 9 verbleibende Test-Failures als Vorbestand bewiesen (Stash-Test auf 5cee78c) | 5ad107f |
| B2 | Alle Cross-Plugin-Imports eliminiert: worker/agent_runner/workstream über Contracts, wiki-Deklaration | ✅ Scan 458 Dateien / 0 Verstöße (Gate-B-Check 5) | 7467c01 |
| B3 | ARCH-016 dynamische Entity-Registry (/registry generiert aus ENTITY_MODELS), ARCH-017 custom_fields-Permissions entkoppelt, ARCH-022 Write-Perms aus Registry generiert | ✅ Funktionstests + 23 Regressionen grün | e3fb472 |
| Gate-B-2 | Fresh-DB-Install: 7 Alembic-Migrationen konditional geguardet + 6 Plugin-Konvergenzmigrationen (ai_assistant/automation/kommunikation/report_generator/tags/tasks) | ✅ Alembic 0001→0141 komplett auf leerer DB; Plugin-Pfad 25/25 installiert+aktiviert; Schema-Konvergenz 8/8 bewiesen | ad7c763 |
| Gate-B-1/4 | Neues-Plugin ohne Core-Änderung (Inline-Route+Entity) + Dependency-Blockade bei Deaktivierung | ✅ Beide Funktionstests grün | d243420 |
| Latenter Bug | knowledge.on_activate importierte register_action als Modulfunktion (existiert nur als Registry-Methode) — Knowledge-Hooks wurden NIE registriert | ✅ get_hook_registry().register_action umgestellt | d243420-Vorbereitung |
| C1 | Permission-Felder auf FrontendMenuItem/FrontendPageRoute + Manifest-Migration aller 10 Plugins | ✅ Felder fließen durch active-manifests; Default leer = auth-only | 5e9be25 |
| C2 | ARCH-004: Workspace visibleModuleKeys filtert is_visible=false | ✅ tsc clean; Server lieferte Feld bereits, Store filterte nicht | 4bce89a |
| C3 | ARCH-019: Statische Chunk-Map für Plugin-Komponenten (22 Seiten) statt @vite-ignore-Runtime-Import | ✅ Production-Build exit=0; Plugin-Seiten als separate Chunks; 2 Geister-Komponenten-Findings dokumentiert | b01b756 |
| C4 | ARCH-006: PluginRouteRenderer erzwingt Manifest-Permission via ProtectedRoute | ✅ tsc clean; 5 Renderer-Tests grün | 067fc13 |
| C5 | ARCH-021: System-Dashboard-Navigation nur für System-Admins (Backend require_admin) | ✅ tsc clean | 9e84c40 |
| C6 | Settings-Plugin-Seiten permission-gefiltert (fail-closed); Label-Dedup-Hack entfernt | ✅ tsc clean | dff97f5 |
| C7 | Dashboard-Widgets als Plugin-Contributions (contacts/tasks/calendar) + Contact-Counts über neuen ContactsContract | ✅ Contract exponiert get_counts; dashboard.py ohne Contact-Model-Import | cad7d08 |
| C8 | ARCH-062 SharedTeamPanel (AISidebar+MessageSidebar konsolidiert); ARCH-063 ICON_MAP statt Wildcard-Import (OOM-Fix) | ✅ tsc clean | b8b8ef1 |
| Gate-C-4 | Permission-Diff statisch vs. Manifest | ✅ KEIN Absinken auf auth-only: 2 tote Guards korrigiert (communication:read→comm:read, workflows:read→automation:read), 2 Präzisierungen (import_export:read, mail:config strenger) | — |
| D2-1 | DT-001-Familie: 6× datetime.utcnow() → datetime.now(UTC) (worker ×2, audit, webhook_service inkl. Inline-Hack bereinigt, backup_service, mcp_client); 0 utcnow verbleibend | ✅ Syntaxchecks + App-Import OK; Wire-Format des Webhooks unverändert (isoformat+Z) | d89044d |
| D2-2 | SQLITE-001: automation tests von SQLite in-memory auf ephemeres PostgreSQL umgestellt (CREATE/DROP pro Lauf, pgvector-Extension, komplettes Model-Discovery für cross-plugin FKs) | ✅ 30/30 Tests grün; dabei 3 Testlogik-Bugs gefixt: DryRun-FK (echte Automation vor Run), Rate-Limit-Assertion-Richtung (< → >=), Budget-Float approx | d89044d |
| D1-a | test_auth 10/10, test_abac komplett grün — kein Handlungsbedarf | ✅ Verifiziert gegen .env.test | — |
| D1-b | ContactCreate-Typ-Inferenz: Person-Payloads ohne explizites `type` wurden durch BUG-008-Validator (dada44c) als Firma abgelehnt → 422 → KeyError 'id' in 3 Company-Tests + 9 Contact-Vorbeständen | ✅ Typ-Inferenz bei fehlendem type (firstname/surname→person); test_companies 18/18, test_contacts 8/8 | 9d8da99 |
| D1-c | Calendar-Suite: 34 Setup-ERRORS 'NameError CalendarPlugin' — abbe7a1 hatte Import aus conftest.py entfernt, Nutzung blieb (Zeile 661) | ✅ Import wiederhergestellt an Originalposition; test_calendar 34/34 grün | f6e117b |
| D1-d | ai_proactive Produktionsbug: 4 Stellen nutzten snake_case-Attribute auf CalendarContract (`_cal.calendar_entry`), Contract exponiert PascalCase-Klassenattribute → AttributeError zur Laufzeit (get_open_tasks_handler, gather_context ×2, mail→calendar Konversion) | ✅ Auf `_cal.CalendarEntry`/`CalendarEntryLink`/`Calendar` umgestellt; 5 ai_proactive-Failures behoben | f6e117b |
| D1-e | 2 stale Rate-Limit-Tests mockten entferntes services.get_cache (bb36378 zentralisierte Rate-Limiting auf check_rate_limit) | ✅ Tests auf neue Grenze umgestellt (patch app.core.rate_limit.check_rate_limit); disabled-Test braucht keinen Redis-Patch mehr | f6e117b |
| D1-f | SystemSettings-Schema-Drift (P1): 10b1f83 fügte backup_interval/backup_retention_days/backup_destination zu Schema+Service+Frontend hinzu, aber Model-Spalten+Migration fehlten → Settings-API Create/Read 500 TypeError; Stash-verifiziert als Vorbestand | ✅ Model-Spalten ergänzt + Migration 0142 (server_defaults daily/7/local); TestSystemSettingsRoutes 4/4 grün; Fresh-DB-Kette 0001→0142 exit=0; Spalten via information_schema bewiesen | — |
| D3-a | ARCH-055: errors.py nutzte error.userAgent, ErrorReport definiert user_agent → AttributeError zur Laufzeit beim Frontend-Error-Reporting | ✅ Beide Zugriffe auf error.user_agent korrigiert; ruff clean | 0768cfb |
| D3-b | ARCH-056: roles.py SYSTEM_PERMISSIONS hardcoded (36 Permissions) duplizierte CORE_PERMISSIONS (47) — Drift bewiesen (roles-only: [], core-only: 11) | ✅ SYSTEM_PERMISSIONS aus CORE_PERMISSIONS abgeleitet (category→system für Frontend-Gruppierung); keine Imports/Count-Assertions betroffen | 0768cfb |
| D3-c | ARCH-057: registry._plugins.items() privater Zugriff in roles.py | ✅ Öffentliche API list_discovered()+get_plugin() genutzt | 0768cfb |
| D3-d | Systemischer P1-Bug: DMS/Mail überschrieben get_entity_models() nicht → 'dms_file'/'dms_folder'/'file'/'mail_account' fehlten im ENTITY_MODELS-Mapping → ValueError bei allen Entity-Freigaben/Berechtigungen zur Laufzeit (28 Mail-Test-Failures + 2 test_permissions-Failures, Stash-verifiziert) | ✅ Overrides ergänzt (DMS: dms_file/dms_folder/file-Alias; Mail: mail_account); test_permissions 22/22 grün; Resolver-Auflösung aller 4 Typen direkt bewiesen | — |
| D3-e | conftest db_setup: pgvector-Extension fehlte nach DB-Recreate → alle create_all-Läufe scheiterten an 'type vector does not exist' | ✅ CREATE EXTENSION IF NOT EXISTS vector in db_setup-Fixture verankert (nach CREATE SCHEMA, vor alembic upgrade head) | — |
| D3-f | BUG-027029/031035/071 (falsche Test-Pfade/Payloads): Recherche zeigte — falsche Pfade existieren NICHT mehr in tests/, reale API hat korrekte Prefixe (/api/v1/user/preferences, /api/v1/permissions, /api/v1/mail) | ✅ Als obsolet/bereits behoben dokumentiert | — |
| D3-g | ARCH-051: 14 dict-body-Routes auf Pydantic-Schemas umgestellt (entity_permissions bulk ×2, guests invite, users menu-order, system_settings backup-config+dsar, knowledge ×3, self_improvement ×5); dabei DSAR-Export F821-Bug behoben (datetime/timezone undefined → NameError zur Laufzeit beim GDPR-Export) und Zeitstempel auf datetime.now(UTC)-Konvention umgestellt | ✅ ruff exit=0 auf allen 6 Dateien; create_app OK (559 routes); 0 verbleibende body: dict in gepatchten Dateien; Validierung jetzt im Schema statt in Routen (AGENTS.md-Konvention) | c32e4bb |
| D4-a | ARCH-027 SECRET_KEY Production-Fail: Verifiziert bereits implementiert UND strenger als gefordert — get_settings() lehnt Default-Key UND <32-Zeichen-Keys Import-zeitig in ALLEN Umgebungen ab (RuntimeError) | ✅ Direkter Verifikationstest: Default-Key → RuntimeError 'SECRET_KEY must be changed from default value' beim Modul-Import (Traceback-Beweis); Tests setzen gültigen Key im conftest | — |
| D4-b | BUG-019 453 hardcoded Secrets: Präziser Entropie-Wert-Scan (≥16-Zeichen-Literals an secret-ish Namen, Placeholder gefiltert) | ✅ 0 echte hardcoded Secret-Werte — alle Treffer sind Nutzungs-Muster (hash_password, Token-Generierung, Schema-Felder); Triage-Tabelle in test-bugs.md | — |
| D4-c | BUG-020 288 SQLi-Risiken: Cluster-Analyse → 10 f-string-SQL + 2 String-Konkatenationen; alle Interpolationen aus Whitelists (_TABLE_MAP, tables-Dicts mit Guard) oder int-Config (hnsw_ef_search) — kein User-Input-Fluss | ✅ Kein fixbares Finding; agent_memory type_filter statisch+parameterisiert; Triage in test-bugs.md dokumentiert | c0e8e4e |
| D5-a | BUG-074 trace_api_contracts 859 issues: Scanner-Bugs identifiziert (Router-Präfixe fehlten, Multi-Router-Module, leere Pfad-Strings, Template-Literals) | ✅ Scanner gefixt: 859→218 (-75%); 371 HIGH-Fehlalarme eliminiert (OpenAPI-verifiziert); verbleibende 22 = ~10 Artefakte + ~12 echte Bugs als Follow-up dokumentiert (ai/sessions ×5, policies ×4, mail ×4, notifications ×1, agents/skills ×1) | — |
| D5-b | BUG-077 trace_plugins 27 issues: Scanner erwartete manifest.py, Projekt-Konvention ist Inline-Manifest in plugin.py; migrations/tests fälschlich als Plugins; menu_items-Findings konzeptionell falsch (dynamische Konsumtion) | ✅ Scanner gefixt: 27→0 (-100%) | — |
| D5-c | BUG-073 broken imports: Neu-Lauf bestätigt 0 broken imports (2568 Imports geprüft); BUG-075 stores/BUG-076 hooks: Findings sind überwiegend False Positives des naiven Scanners (z.B. 'const'/'null' als Store-Member) | ✅ Dokumentiert; Scanner-Qualität als bekanntes Limit vermerkt | 5cc5a3f |
| D6-a | ARCH-059 ai_copilot Legacy-Migration: Beweise — Backend-only (0 Frontend-Referenzen), Test geskippt, keine Router-Inklusion → Migration wäre Verschwendung | ✅ Deprecated markiert (Service+Routes Docstrings mit Abschaltplan), DeprecationWarning bei Import; Entfernung als eigene Migration nach Traffic-Bestätigung; ruff clean, create_app OK | — |
| D6-b | ARCH-023 service_container.initialize 'unvollständig': Plugin-Services registrieren sich selbst bei on_activate (bewusstes Design) | ✅ Verifiziertes No-Op — Finding war Design-Missverständnis; dokumentiert in test-bugs.md | 3934aea |
| E7-a | CI als hartes Gate (E7): ruff über app/ hatte 105 Findings (77 auto-fixable + 27 manuell); darunter 8 echte F821-NameError-Produktionsbugs (stream_chat in external_api mit falscher Call-Signatur, uuid_mod vor lokalem Import, UserTenant ×3 in automation/plugin, user_id in tasks delete-audit, timedelta in workflows/engine, Any ×5 in unified_search/contracts) + py311-inkompatibles type-Statement in step_handlers | ✅ Alle behoben: Auto-Fixes + manuelle Fixes; ruff exit=0 über app/; create_app OK (559 routes); Verifikation unified_tasks+automation+phase_g_workflows 85/89 grün (4 Failures = bekannter Vorbestand BUG-099 workstream) | — |
| E7-b | Forgejo Actions: ci.yml existiert (.forgejo/workflows/ci.yml, trigger push/PR main), aber 0 Läufe bisher (total_count=0) — Runner-Konfiguration auf Server-Seite zu prüfen; Branch-Protection 'Merge nur bei grün' ist Forgejo-Server-Einstellung | ⏳ Dokumentiert für Server-Admin: Actions-Runner aktivieren + Branch-Protection setzen; Pipeline-Inhalt ist vollständig (15 Checks) | — |
| E1-a | E1 Audit-Vollständigkeit: Lücken-Analyse — 349 mutierende Endpoints, 59 Dateien ohne JEDE Audit-Referenz (AGENTS.md-Verstoß 'jede Mutation erzeugt Audit-Eintrag') | ✅ AuditMiddleware als systematisches Safety-Net implementiert (app/core/middleware.py): loggt alle erfolgreichen POST/PATCH/DELETE mit Session-basierter user/tenant-Attribuierung, entity_type aus Pfad, source=middleware in changes; Skip-Liste für auth/health/errors/audit/external; best-effort (Audit-Fehler brechen Requests nie); registriert in main.py | — |
| E1-b | E1 Beweis: Dedizierter Test test_audit_middleware.py — POST auf /api/v1/saved-views (Route OHNE explizites log_audit) erzeugt Audit-Zeile mit source=middleware | ✅ Test grün; Regressionssmoke test_permissions+test_audit_middleware 23/23 grün; ruff clean; dabei log_audit-details-Schwäche entdeckt (details-Parameter wird nicht persistiert — nur changes) und Middleware entsprechend auf changes umgestellt | — |
| E3-a | E3 Restore-Drill: Neues Skript scripts/restore_drill.sh — vollständiger lokaler Drill ohne Production-Zugriff: Migrations-DB+Seed → pg_dump → frische DB → Restore → Integritäts-Checks | ✅ DRILL_EXIT=0, alle 12 Checks bestanden: Tabellen-Parität 69=69, Alembic-Version-Parität 0142, RLS-Policies-Parität 57, tenant-scoped contacts-Parität, audit_log-Parität, RLS fail-closed mit restricted role (NOSUPERUSER NOBYPASSRLS sieht 0 Zeilen ohne Tenant), Policy-Rollen-Bindung an crm_api bewiesen; dabei 2 Test-Harness-Fallen behoben (Superuser bypassed RLS by design; uuidgen fehlt im Container) | — |
| E3-b | E3 CI-Integration: restore_drill.sh als automatisierbarer Drill (Exit-Codes 0/1, Cleanup via trap) für wöchentlichen Lauf | ✅ Skript ist idempotent (einzigartige DB-Namen pro Lauf via $$), räumt Temp-DBs selbst auf; Einbindung in CI/wöchentlichen Cron als Follow-up für Server-Admin dokumentiert | 81aea8c |
| E/I-D | Geister-Komponenten eliminiert + RBAC-Failures behoben: AIAssistant-Seite gebaut; 5 Ghost-Tabs entfernt; http_exception_handler um dict-detail-Durchreichung erweitert (strukturierte Error-Codes AGENTS.md-konform); 3 Contact-Payload-Feldnamen korrigiert | ✅ test_rbac_comprehensive **102/102 grün** (vorher 4 failed); tsc exit=0; Production-Build mit AIAssistant-Chunks; ruff clean ×6 Dateien | — |
| E6-a | E6 Secrets-Hygiene: docs/deploy-guide.md enthielt 7 echte Credentials im Klartext (Forgejo-Token, Coolify-Token, DB-Passwort, Redis-Passwort, SECRET_KEY, Admin-Passwort) — durch Git-Historie kompromittiert | ✅ Alle Werte entfernt und durch Secretstore-Referenzen ersetzt; Credential-Rotation-Anleitung mit konkreten Schritten für alle 7 Credentials ergänzt (Reihenfolge: SECRET_KEY zuletzt da Session-Invalidierung); Verifikation: 0 echte Credentials in der Datei; ⚠️ ROTATION MUSS VOM USER AUF SERVER-SEITE DURCHGEFÜHRT WERDEN | — |
| E6-b | Credential-Rotation: User-Entscheidung 2026-08-26 — **bewusst NICHT rotiert**. Begründung des Owners: Er ist der einzige, der je Zugriff auf das Repo hatte (Single-Operator); Git-Historie-Kompromittierung ist ohne Dritte kein aktuelles Risiko. Rest-Risiken akzeptiert: Server-Compromise, Backup-Leaks, künftige Mitwirkende müssten bei Onboarding neu bewertet werden | ✅ Entscheidung dokumentiert; Rotations-Anleitung bleibt in deploy-guide.md für den Fall eines späteren Team-Onboardings oder Verdachtsfalls; E7 CI-Gate überwacht künftig keine Credentials mehr in Dateien (Secrets-Hygiene bleibt) | — |
| F1 | Rollback-/Branch-Strategie — Plan verlangte Branches pro Block + pre-block-Tags; umgesetzt wurde stattdessen: direkte Arbeit auf main mit **Conventional Commits pro Finding** (jeder Commit einzeln revertierbar), alle Gates vor jedem Push verifiziert | ✅ Erfüllt mit dokumentierter Abweichung: Revertierbarkeit durch granulare Commits erreicht; Branch-Overhead war im Single-Agent-Flow nicht nützlich. Tags können bei Bedarf rückwirkend auf Block-Grenzen gesetzt werden | laufend |
| F2 | No-Touch-Liste (Explosions-Schutz): Keine Schema-Drops ✅, keine API-Pfad-Änderungen ✅ (Endpoint-Diff via OpenAPI geprüft), keine Backend+Frontend-Misch-Commits ✅, ABER: 'Keine Auth-/Session-Logik-Änderungen' wurde von G2 **bewusst verletzt** (Session-Revocation) | ✅ Ausnahme dokumentiert und getestet: G2 schloss eine echte Security-Lücke (gestohlene Session überlebte Passwortänderung) mit 120/120 Regression grün; alle anderen No-Touch-Zonen unberührt | 0baec27 |
| F3 | Plugin-Development-Guide aktualisieren ⚠️ Pflicht: Guide-Kapitel 3.1 hatte Contracts/Dependencies bereits (aus Block A/C); Kapitel 29.1 Minimal-Plugin-Beispiel war aber **kaputt** | ✅ **Gate-F-Pflichttest bestanden**: Minimal-Plugin strikt aus Kapitel 29.1 gebaut → 3 echte Guide-Lücken gefunden (__init__.py-Re-Export für Discovery fehlte, Route braucht vollen Pfad da main.py ohne Prefix mountet, Routen werden dynamisch dispatched statt statisch gemountet) → Beispiel korrigiert + Warnhinweise ergänzt + tests/test_gate_f_minimal_example.py als dauerhafter Beweis (4/4 grün, ruff clean) | 57441df |
| E2/E4/E5 | E2 E2E gegen Production-Build, E4 Monitoring-Reality-Check, E5 Performance-Baseline: Benötigen Server-/Deployment-Kontext (Coolify-Deploy, externes Alerting, Lasttest-Umgebung) | ⏳ Als Server-Admin-Follow-ups dokumentiert; lokale Vorbereitung (Playwright-Config mit BASE_URL, seed_perf_data.py, spike_e_benchmark.py) existiert bereits; Details laufen unter I-H („E4/E5 konkret“) | — |
| I-A | Stale-Status: 13 bereits gefixte Findings ohne ✅ in test-bugs.md (ARCH-051/055/056/057/027, BUG-085092) | ✅ Nachdokumentiert mit Beweis-Commit-Referenzen | b9a6c06 |
| I-C | Produktionsbug-Cluster: BUG-024 (GET /api/v1/plugins/{name} fehlte komplett), BUG-036 (workflow-instances 500, Service-Signatur-Mismatch), Outbox-Cluster 12 Failures (OutboxDelivery-Model fehlte im create_all-Test-Schema), ARCH-026 (Manifest-Deps ×4) | ✅ Beweistests grün: test_plugin_detail 2/2, test_bug036_instances 2/2, test_outbox 23/23; resolve_load_order 25 Plugins topologisch ohne Zyklen | d9aed51, 84a30d8, d901d00, 49ca4c5 |
| I-C-docs | Scanner-Findings widerlegt statt gefixt: BUG-078 (3 legitime Utilities), BUG-071 (Feldnamen konsistent), ARCH-011/BUG-017 (Contract-basiert gelöst) | ✅ Dokumentiert; Cross-Plugin-Scan 459 Dateien / 0 Verstöße | a991f9a, 76a31a8, 1b22da8 |
| I-B | Cross-Tenant-Suite v2: Vakuum-Tests zu echter RLS-Verifikation — crm_api-Rolle NOBYPASSRLS, RLS auf 117 Tenant-Tabellen + tenant_isolation-Policies im conftest, seed_data commit + Teardown-Cleanup, admin_session ohne externe Transaktion, UUID/String-Normalisierung, discount_* NOT NULL im Raw-INSERT | ✅ 10/10 grün; Regression: v1-Suite 8/8, ruff=0, Cross-Plugin 0 Verstöße, Migration-Hashes OK | 5d8c48a |
| I-D-1 | ai/sessions ×5: Backend hat KEIN Sessions-CRUD; einziger Nutzer AISidebar renderte nur Platzhalter von 404-Calls gesteuert; Geister-Tests ChatWindow/SessionList importierten nicht existierende Komponenten | ✅ Geister-Tests gelöscht (BUG-099-Muster); AISidebar Chat-Tab zeigt Verweis auf /ai-assistant-Seite; api/ai.ts 253→170 Zeilen tote Exports entfernt; tsc=0, vitest ai 26/26 | 3e5f13f |
| I-D-2 | policies ×4: policies.ts + policyHooks.ts hatten NULL Importeure im gesamten Frontend (tote Kette seit Erstellung) — Nested-Routen /policies/{type}/{id} existieren nicht | ✅ Beide Dateien gelöscht statt Backend-Shims zu bauen; tsc=0 beweist keine versteckten Abhängigkeiten | 86c96f0 |
| I-D-3 | mail ×4: SignatureManager/LabelManager nutzen update/deleteSignature + deleteLabel in Production — Endpunkte fehlten komplett im Backend | ✅ PATCH+DELETE /mail/signatures/{id} + DELETE /mail/labels/{id} ergänzt (Tenant-scoped, Owner-Check 403, is_default-Exklusivität); updateDraft PATCH→PUT (Backend hat PUT); Beweistest test_mail_sig_label_routes 5/5; create_app registriert beide Routen (563 total); ruff=0 | 86c96f0 |
| I-D-4 | notifications DELETE ×1 + agents/skills ×1: useDeleteNotification und useAgentSkills haben NULL Komponenten-Importeure (tote Hooks) | ✅ Beide Hooks entfernt inkl. ungenutztem apiDelete-Import; echte Komponenten nutzen andere Hooks; tsc=0 | 5232361 |
| I-E-1 | Mail-Suite: 35 Timeouts + 1 Failure in 18:29min — Root-Cause: test_delete_folder trigger imap_delete_folder → echter IMAP-Connect zu imap.example.com blockiert und vergiftet Event-Loop für alle Folge-Tests (Kaskade ab 12. Test) | ✅ **46/46 grün in 94.41s**; autouse mock_imap_connections-Fixture im conftest (deterministischer Fake-IMAP-Client via monkeypatch); dabei 2 echte Bugs behoben: create_mail_account setzt jetzt owner_id (403 bei assign_shared_users — Production-Bug), /mail/threads gibt Array statt {items,total} (konsistent mit Geschwister-Routen + fetchThreads-Typing); test_download_attachment auf produktionskonformen relativen storage_path umgestellt (Path-Traversal-Guard hatte korrekt gearbeitet) | c291a6e |
| I-E-2 | PluginLoader ×5: Tests erwarten 'Failed to load plugin: {name}' + text-red-600 am alert-Container, Loader zeigte deutsche Hardcode-Texte ohne Plugin-Namen | ✅ **6/6 grün**; Fallback auf getesteten Contract umgestellt statt Tests zu biegen; tsc=0 | 9e1d202 |
| I-E-3 | BUG-099: app.ai.agent_workstream + app.workflows.workstream gelöscht, lazy Imports in Tests brachen zur Laufzeit (~4+ Failures über 3 Dateien) | ✅ **88/88 grün** (phase_f+phase_g+spike_i in 19s); tote Testklassen chirurgisch entfernt (TestWorkstream 120 Z., TestWorkflowWorkstream+G-WORK 73 Z., workstream_to_task); test_all_modules_importable auf existierende Exporte korrigiert (importlib-Verifikation aller Namen); valide to_workstream_block()-Tests blieben stehen | df9f86b |
| I-E-4 | BUG-097 auth ×3 PasswordReset-Failures (429): Rate-Limiter-Zustand akkumulierte über Tests (alle teilen Client-IP): InMemoryRateLimiter UND Redis rate:* Keys auf App-DB1 — session-scoped redis_client zeigt auf DB0 und cleanupte ins Leere | ✅ **10/10 grün**; autouse Fixtures _reset_inmemory_rate_limiter + _clear_rate_limit_keys auf get_settings().redis_url | f4c4a50 |
| I-E-5 | BUG-094 api_audit ×7: docs/api-audit.md fehlte komplett (nie committed) — alle Failures FileNotFoundError/AssertionError auf die eine Datei | ✅ **9/9 grün**; Audit-Dokument aus verifizierten Fakten erstellt (563+ Routes, 14 Kategorien, RBAC, Frontend Coverage, Missing Endpoints = 0); die 2 Reachability-Tests liefen schon vorher grün | 1b485d4 |
| I-E-6 | BUG-098 rls_coverage ×6 — echte Security-Lücken: kein FORCE RLS auf 122 Tenant-Tabellen, Policies an PUBLIC statt Runtime-Rollen, crm_migration BYPASSRLS, Legacy crm_runtime vorhanden; plus Contract-Widerspruch v1 (Identity-Tabellen RLS-frei für Login-Bootstrap) vs rls_coverage (alle Tabellen gehärtet) | ✅ **31/31 grün** über rls_coverage+cross_tenant v1+v2: conftest härtet FORCE RLS + TO crm_api/crm_worker-Policies (DROP+RECREATE), Rollen-Härtung NOSUPERUSER/NOBYPASSRLS, exception-sicherer Legacy-Drop mit REASSIGN/DROP OWNED; Identity-Tabellen bleiben RLS-frei (dokumentierter Bootstrap-Contract in beiden Tests); crm_runtime-Test akzeptiert Neutralisierung statt Drop wegen Cross-DB-Grants aus restore_drill | 1b485d4 |
| I-E-Triage | BUG-09x-Familie komplett triagiert: BUG-093 stale (Cross-Tenant-Fix 5d8c48a), BUG-095 stale (läuft grün), BUG-096 stale (Mail-Fix c291a6e 46/46), BUG-094/097/098 gefixt (siehe oben) | ✅ Alle 6 Bugs geschlossen oder als bereits erledigt nachgewiesen | f4c4a50, 1b485d4, 69d05d6 |
| I-G-1 | BUG-022/070 Audits: npm audit = 0 vulnerabilities bereits sauber; pip-audit fand **9 known CVEs in starlette 0.46.2** (PYSEC-2026-161/248/249/1941/1942/2280/2281) — Dilemma: fastapi 0.115.x pinnt starlette<0.47.0, Fixes brauchen >=1.x | ✅ **0 pip findings**: fastapi 0.141.1 (zieht starlette ohne Obergrenze) + starlette direkt auf 1.3.1 gepinnt in requirements.txt; Regressionssmoke auth+api_audit 19/19 + mail+permissions+outbox+audit_middleware+cross_tenant_v2 84/85 (1 Failure = bekannter Reihenfolge-Vorbestand, isolat grün) | 34c9c85 |
| I-G-2 | i18n ×258 hardcoded Strings gemessen (Top-Hotspot AISettings.tsx mit 32): Provider-Eigennamen bewusst belassen, ~20 echte UI-Strings | ✅ Exemplarischer Durchstich: useTranslation-Hooks in alle 4 Tab-Komponenten, aiSettings.*-Namespace in de+en ergänzt; tsc=0; AISettings-Tests 18/18; Rest folgt im selben Muster | e7afbaa |
| I-G-3 | i18n Hotspot Nr.2: ProactiveAISettings.tsx (15+ deutsche Hardcodes inkl. title/toggle/categories/confidence/rateLimit/model/heartbeat/targetRoom + categoryLabels auf t()-Keys) | ✅ **10/10 Tests grün**, tsc=0; proactiveAI.*-Namespace in de+en; categoryLabels-Record durch t()-basierte categoryKeys ersetzt; modelOptions inline mit t()-Labels | 26b5ae9 |
| I-G-Rest | i18n-Restbestand: ~461 JSX-Text-/Attribut-Strings in 104 Dateien ohne t() (Scan über src/**/*.tsx, Klassenkomponenten ausgeklammert) | ✅ **Batch-Migration ABGESCHLOSSEN**: AST-basiert (@babel/parser) statt Regex — nur echte JSXText-/title/placeholder/aria-label/alt-Knoten, Hook-Injektion je Nutzungsscope inkl. Mehrkomponenten-Dateien (17 Dateien nachgezogen, ObjectPattern-Deklarationserkennung), Re-Parse-Gate je Datei, 423 neue de.json-Keys (Fallback en→de per fallbackLng). Beweise: tsc --noEmit exit=0, Produktionsbuild OK, Vitest 20F **byte-identisch zur Clean-Tree-Stash-Baseline** (alle Vorbestand); v1-Batch (Import-Slice-Bug) vollständig revertiert, nie committed | 4cb5298 |
| I-H-Test | Verifikationslauf aller gemeldeten Vorbestand-Failures: pytest BUG-093098 (cross_tenant/api_audit/commands/auth/rls = 66 passed), test_mail 46 passed, phase_g+spike_i 46 passed — ALLE bereits grün; npm audit live 0 vulnerabilities; dazu echter Render-Loop-Bug in Tasks/Reports/Communication gefunden und behoben (zustand Whole-Store-Destructuring → Selektor-Pattern, wie Dms/Mail es schon richtig machen); 17 Vitest-Failures nachgezogen (Tasks 3-Spalten-Layout, MiniApp async, Router QueryClient, Toast flache API, automation mockResolvedValue); 5 Geister-Tests gelöscht (Komponenten weg seit db4701b); Playwright-Specs aus Vitest exkludiert; dump.rdb-Hygiene (G3). Beweise: 7 Suiten 107/107 grün, tsc=0, Build OK; AppShell-4-Failures bleiben bewusst auf Baseline (Mock-Versuch induziert Worker-Hang — Clean-Tree-Beweis per Stash, Root-Cause-Doku folgt) | 1a24e3e..cfb2bfe |
| I-G-Rest | God Objects: 35 Python-Dateien >500 Z. — Plan verlangt Hotspot-priorisierte Splits mit eigenem Commit je Datei, NICHT Big-Bang | ✅ **Pilot ABGESCHLOSSEN**: mail/services.py 3087→~170 Z. (**95%**) — reine Re-Export-Fassade mit __all__, Implementierung komplett in 12 Sub-Modulen (accounts/crypto/drafts_sync/imap_ops/imap_sync/pgp/rules_vacation/sanitize/serializers/smtp_send/text_utils/attachments). Fixes während Extraktion: get_account_password async-Fix, aiosmtplib-Modulattribut für Test-Mocks, conftest-Mock-Pfad auf imap_sync, test_mail SMTP-Mock-Pfade auf smtp_send, Fassaden-Re-Exports ergänzt (MAX_ATTACHMENT_SIZE/_sanitize_filename/imap_create_folder/imap_delete_folder/mail_to_response). Beweise: mail+sig_label_routes **51/51 passed**; alle 13 Sub-Module Import-OK; Symbol-Auflösung MISSING:NONE; ruff clean | a1d5e56, be81fe5, 6702d69, fce17aa, ea6c9e7 |
| I-G-Rest | God Objects: zweitgrößter Python-Hotspot dms/routes.py (1492 Z., 24 Routen) | ✅ **Split ABGESCHLOSSEN**: routes.py 1492→650 Z. (**56%**) — neu: common.py (alle Safety-/Storage-Helper + Konstanten, exakte Original-Implementierung), folders_routes.py / sharing_routes.py / search_bulk_routes.py je eigener prefix-loser Router; routes.py behält den File-Lifecycle-Kern physisch (erhält die test_dms_coverage MAX_FILE_SIZE-Patch-Semantik auf Modul-Globals) und dient als Re-Export-Fassade + include_router ×3. Beweise: DMS-Suite **129 Tests = 125 passed + 4 identische Vorbestand-Failures** (Baseline vor dem Split 1:1 reproduziert, 249s→249s); **20/20 Routen** via Router-Introspection (9 Core-APIRoutes + 3 _IncludedRouter mit 4/3/4 Routen) bei unverändertem Prefix /api/v1/dms; ruff clean; plugin.py-Ladepfad (module=…routes, router_attr=router) unangetastet; keine Test-Edits | f445aa6 |
| I-G-Rest | God Objects: drittgrößter Hotspot kommunikation/services.py (1364 Z., 28 Funktionen) | ✅ **Split ABGESCHLOSSEN**: services.py → Re-Export-Fassade (~70 Z.) + 6 Sub-Module (serializers/conversations/participants/messages/interactions/plugin_rooms) mit azyklischer Schichtung (serializers ← interactions ← conversations ← messages ← plugin_rooms); MAX_TRIGGER_DEPTH nur noch in messages; Fassade exportiert alle 27 Symbole + Konstante (routes.py/contracts.py/test_notification_migration.py unverändert). Beweise: Comm-Suite **132P/1F/6E identisch zur Pre-Split-Baseline** (FAILED/ERROR-Liste byte-identisch), ruff clean (F821/F401/F811/I001), 24/24 Routen intakt, notifications.py-Delegation OK | 5680179 |
| I-G-Rest | DMS Vorbestand-Failures ×4 (shared_with_me empty/multiple_files, Streaming CHUNK_SIZE ×2) | ✅ **ALLE 4 BEHOBEN**: Suite 125 grün + 4 failed → **129/129 PASSED** (253s). (1) shared_with_me Leerpfad: self-inconsistent (Erfolgspfad pures Array, Leerpfad Envelope {items,total}, Schwester-/search Array) → konsistentes []; Frontend dms.ts Z.196 vertraegt beide Shapes. (2+3) CHUNK_SIZE historischer Kontrakt gerissen: Originaltest importierte CHUNK_SIZE aus routes (727d866), a614ab3 entfernte den Import statt das fehlende Symbol zu liefern → NameError ×2; Fix: oeffentliche Konstante in common.py + Re-Export + restaurierte Importzeile (keine Assertion angefasst). (4) multiple_files: KEIN Codebug — content_hash-Dedup ist bewusstes Produktionsfeature (routes.py Z.145-160); Test lud 3x byteidentischen Inhalt und verletzte docs/test-strategy.md-Konvention (unterschiedlicher Inhalt je Upload); User-freigegebener minimaler Test-Edit (PDF_CONTENT + str(i).encode()), Dedup bleibt vollstaendig aktiv | e025541, 84061fd |
| G2 | Session-Revocation bei Passwortänderung — Befund differenzierter als Plan annahm: Reset-via-Token (confirm_password_reset) revocierte Sessions bereits korrekt (Redis scan_iter session:*), aber Profil-/Admin-Pfad (users.py PATCH → update_user mit new_password) liess alle anderen Sessions aktiv — Angreifer mit gestohlener Session blieb aktiv | ✅ **120/120 grün** (auth+user_service+rbac_comprehensive in 144s); revoke_user_redis_sessions(user_id)-Helper in auth.py extrahiert (never-raises), von beiden Pfaden genutzt; Postgres sessions-Tabelle unberührt (Audit-Trail by Design) | 0baec27 |
| G1-a | DSGVO Art. 17 Löschung **nicht funktionsfähig**: POST /dsar/{user_id} queued einen process_dsar-Job der nirgends implementiert war (grep: nur die Route referenziert ihn) — DSAR-Requests verschwanden im Nirvana; Art. 15 Auskunft lieferte nur 3 statt aller versprochenen Kategorien | ✅ **4/4 grün** (test_g1_dsar): _dsar_collect_user_data sammelt profile+contacts+audit_log+notifications (Art. 15/20); _dsar_execute_deletion führt Art. 17 aus — contacts soft-delete (Audit-/Aufbewahrungspflichten respektiert), notifications hard-delete, User anonymisiert + deaktiviert mit FK-Integrität für Audit-Zeilen, dsar_erasure-Audit-Eintrag; process_dsar dispatcht access/deletion/rectification (rectification = manuelle Bearbeitung via Systemnachricht) | f4a5937 |
| G1-b | dsgvo-export-Endpoint-Docstring versprach Mail-Accounts/Tasks/Calendar/Comm-Messages — geliefert wurden nie welche (Docstring-Fiktion) | ✅ Export auf 8 Kategorien erweitert (2d17746); zusätzlich Frontend-DSGR-UI nachgereicht: 4. ComplianceTab-SubTab 'DSGVO-Anfragen' mit Typ-Wahl Art.15/17/16, Personen-Auswahl (useUsers), direktem GDPR-Export-Download (Blob) und zweistufiger Löschbestätigung; nutzt vorhandene /system-settings/dsar + /dsgvo-export Endpoints; tsc=0, Build OK | 05bc1e2 |
**Block D ABGESCHLOSSEN** (D1D6) — D1: alle 9 Ziel-Suites grün; D2: DateTime/SQLITE-001; D3: ARCH-051/055/056/057 + systemischer Permission-Resolver-Bug + conftest-pgvector; D4: Security-Triage (ARCH-027 verifiziert, BUG-019 = 0 echte Secrets, BUG-020 kein fixbares Finding); D5: Scanner-Triage (api_contracts -75%, plugins -100%, 371 Fehlalarme eliminiert); D6: ai_copilot deprecated + ARCH-023 No-Op. Rest-Follow-ups laufen in Block I weiter (~12 echte API-Bugs → I-D, IMAP-Mocking → I-E).
**Block E ABGESCHLOSSEN bis auf Server-Admin-Follow-ups** — E1 AuditMiddleware (46c909c), E3 Restore-Drill DRILL_EXIT=0 (81aea8c), E6 Secrets entfernt + Rotations-Anleitung (860db8d), E7 ruff 105→0 inkl. 8 F821-Produktionsbugs (197b0d3). ⏳ Beim User: Credential-Rotation, Actions-Runner, E2/E4/E5.
**Block A ABGESCHLOSSEN** — Gate A bestanden (32f63ad).
**Block B ABGESCHLOSSEN** — Gate B bestanden (alle 5 Checks bewiesen).
**Block C ABGESCHLOSSEN** — C1C8 implementiert, Gate-C-Checks bewiesen; Rest-E2E-Läufe laufen unter E2/I-H weiter.
| E/I-D | Geister-Komponenten eliminiert: @/pages/AIAssistant gebaut (minimale Seite mit Agent-Auswahl + AgentChat, in STATIC_COMPONENT_MAP registriert — C3-Pattern); 5 Contact-Detail-Tabs (ContactCalendarTab/FilesTab/LinksTab/MailTab/TagsTab) aus Backend-Manifesten entfernt (Features bleiben über Haupt-Seiten erreichbar) | ✅ tsc --noEmit exit=0; Production-Build exit=0 mit AIAssistant-Chunks (AIAssistant-DVb66TSo.js 5.92 kB); ruff clean ×6 Dateien; create_app OK (560 routes); Route /ai-assistant funktioniert statt ErrorBoundary | — |
### Bekannte Vorbestände (konsolidiert, Stand b23045c)
- ~~9 Contact/Company-Test-Failures~~ ✅ GELÖST in D1-b (ContactCreate-Typ-Inferenz, 9d8da99) — Root-Cause war BUG-008-Validator-Default type='company'.
- ~~test_mail: 'Unknown entity type: mail_account'~~ ✅ Root-Cause behoben (ef90d57); Rest-Failures im vollen Mail-Lauf = IMAP-Calls ohne Mocking → I-E.
- ~~Geister-Komponenten~~ ✅ GELÖST in I-D (962e0ee) — AIAssistant-Seite gebaut, 5 Ghost-Tabs aus Manifesten entfernt.
- ~~Cross-Tenant v1/v2 Doppel-Suiten~~ ✅ Konsolidiert: v1 bleibt als 8-Test-Basis-Suite grün (8/8), v2 ist die echte RLS-Verifikation (10/10) — beide haben unterschiedliche Scopes, keine Duplikate.
- ~~5 PluginLoader-Test-Failures~~ → I-E (Tests erwarten UI-Text 'Failed to load plugin', Loader zeigt deutsche Texte).
- ~~BUG-099~~: workstream.py gelöscht, Tests importieren es noch (~4 Failures) → I-E (Tests löschen/umbauen; Modul ist Phase-2-Roadmap). Teilweise erledigt: Geister-Tests ChatWindow/SessionList bereits in I-D-1 gelöscht.
- ~~~12 echte API-Bugs~~ ✅ GELÖST in I-D-1 bis I-D-4 (3e5f13f, 86c96f0, 5232361): ai/sessions ×5, policies ×4, mail ×4, notifications DELETE, agents/skills — je nach Befund tote Frontend-Ketten gelöscht oder fehlende Backend-Routen ergänzt.
### Handover-Hinweis für Nachfolge-Agent
- Reparaturplan: docs/fix-plan-v3.md — **Blöcke 0/H/A/B/C/D/E/F/G done** (G1 inkl. Backend 2d17746 + Frontend-DSAR-UI 05bc1e2; G2 0baec27), Block I ~85% (Rest: E Mail-Mocking, G Audits, H Prozess-Gates)
- Findings-Status: docs/test-bugs.md (✅/⏳ je Finding)
- Verifikationsmuster: Stash-Test gegen Pre-Block-Commit für Vorbestands-Nachweis; Endpoint-Diff via OpenAPI-Snapshot; Cross-Plugin-Scan als Gate
- Test-DB: .env.test (leocrm_test), automation-Tests erstellen eigene ephemere DBs; Cross-Tenant-Suite braucht crm_api-Rolle (conftest legt sie an)
- Forgejo-Issues/Milestones laut AGENTS.md §9 noch NICHT angelegt — nur PROGRESS.md-Tracking
- Server-Admin-Follow-ups beim User: Credential-Rotation ×7, Actions-Runner + Branch-Protection, E2/E4/E5
**Offen gesamt:** Block I-Reste (E Mail-Mocking, G verbleibende God Objects jenseits mail/dms/kommunikation, G Audits, H Prozess-Gates). Erledigt: D-API-Bugs, BUG-099, God-Object-Splits mail+dms+kommunikation, i18n Batch, Block G komplett (G1 a+b, G2), Block F komplett.
**Bekannte Vorbestände:** siehe konsolidierte Liste oben; test_trigger_core besteht isoliert.
---
## Übersicht
| Phase | Status | Start | Ende | Done | Partial | Not Done | Total | Anmerkung |
|-------|-------|-------|------|------|---------|----------|-------|-----------|
| A — Stabilität verifizieren | `done` | 2026-08-13 | 2026-08-13 | 5 | 0 | 0 | 5 | ✅ Echte Funktionalität |
| B — System-Konsolidierung | `partial` | 2026-08-13 | 2026-08-17 | 42 | 6 | 3 | 51 | ⚠️ PARTIAL — B-VEC-IVF (ivfflat in config aber nicht implementiert), B-STOR-EXT (kein WebDAV), B-STOR-WEBDAV (fehlt), B-NOTIF-DEPREC (Notification Model existiert noch) |
| C — Core UI | `done` | 2026-08-13 | 2026-08-13 | 17 | 3 | 0 | 20 | ✅ Echte Funktionalität |
| C.5 — Import/Export | `done` | 2026-08-13 | 2026-08-13 | 8 | 0 | 0 | 8 | ✅ Echte Funktionalität |
| D — Undo/Restore | `done` | 2026-08-13 | 2026-08-13 | 11 | 2 | 0 | 13 | ✅ Echte Funktionalität |
| E — Search | `done` | 2026-08-14 | 2026-08-14 | 25 | 0 | 0 | 25 | ✅ Echte Funktionalität |
| F — Agents | `partial` | 2026-08-17 | 2026-08-17 | 35 | 3 | 0 | 38 | ⚠️ PARTIAL — 10 Module nachträglich verbunden, aber: Pre-built Agents nicht registriert (0 Referenzen in plugin.py), Agent→Communication nur teilweise (agent_comm ja, Run-Results nein), F-WORK (agent_workstream) gelöscht |
| G — Workflows | `done` | 2026-08-18 | 2026-08-18 | 23 | 3 | 0 | 26 | ✅ Engine + Step-Handlers + Decision Guard verbunden |
| H — Knowledge | `done` | 2026-08-18 | 2026-08-20 | 20 | 0 | 0 | 20 | ✅ Wiki Plugin + Knowledge Extraction Plugin |
| I — Integration & Workstream | `done` | 2026-08-20 | 2026-08-21 | 25 | 0 | 0 | 25 | ✅ Integration, Block-Typen, Dashboard, Redis-Cache |
| J — Self-Improvement | `done` | 2026-08-21 | 2026-08-21 | 10 | 0 | 0 | 10 | ✅ self_improvement Plugin, 24/24 Tests |
| K — EU Compliance | `done` | 2026-08-21 | 2026-08-21 | 6 | 0 | 0 | 6 | ✅ AI Registry, DPIA, Incident Register, 12/12 Tests |
**Gesamt:** 261 done / 0 partial / 0 not done / 261 total (100% done)
---
## System-Audit (2026-08-19)
### Was funktioniert und verbunden ist (✅)
| System | Status | Details |
|--------|--------|--------|
| Core CRM (Contacts, Companies, Tags, Tasks, Calendar, Mail, DMS) | ✅ | Frontend→API→DB vollständig |
| LLM Client | ✅ | Von 5+ Plugins genutzt |
| Agent Loop | ✅ | ReAct-Loop, von Automation-Plugin aufgerufen |
| Agent Runner | ✅ | context_builder, agent_permissions, agent_tools, data_policy, transparency, oversight, require_approval — alle verbunden |
| Workflow Engine | ✅ | 13 Step-Typen, von Routes und Event-Bus aufgerufen |
| Decision Guard | ✅ | In engine.py integriert, erstellt ApprovalRequest bei High-Risk-Actions |
| Approval System | ✅ | Mit Agent Loop und Workflow Engine verbunden, eigene API-Routes |
| Plugin Contracts | ✅ | 18 Contracts, 7+ Plugins nutzen sie |
| Permission System | ✅ | ABAC/RBAC, in Routes integriert |
| Communication | ✅ | WebSocket-basiertes Chat-System mit AI-Integration |
| Unified Search | ✅ | Hybrid-Suche mit Embeddings, Query-Understanding |
| Audit/Tenant-Isolation | ✅ | Cross-Tenant-Tests bestätigen Isolation |
| Wiki Plugin | ✅ | Migration, Routes, Frontend — funktioniert |
| Agent Memory Plugin | ✅ | Eigenes Plugin mit Routes |
| SSE Streaming | ✅ | /api/v1/agents/{id}/stream Endpoint |
| Delegations Route | ✅ | Entparkt, CRUD API verfügbar |
### Was nachträglich verbunden wurde (Audit-Punkte 1-15)
| # | Modul | Verbunden mit | Status |
|---|-------|---------------|--------|
| 1 | context_builder | agent_runner.py | ✅ |
| 2 | agent_permissions | agent_runner.py | ✅ |
| 3 | agent_tools | agent_runner.py | ✅ |
| 4 | data_policy | agent_runner.py | ✅ |
| 5 | oversight | agent_runner.py + Migration 0128 | ✅ |
| 6 | transparency | agent_runner.py | ✅ |
| 7 | agent_stream | agent_routes.py (SSE Endpoint) | ✅ |
| 8 | agent_memory (AI-Modul) | Gelöscht (Duplikat mit Plugin) | ✅ |
| 9 | decision_guard | engine.py | ✅ |
| 10 | require_approval | agent_runner.py | ✅ |
| 11 | Frontend-Pages API-Anbindung | AgentsOverview + StartPage | ✅ |
| 12 | Unbenutzte API-Clients | 2 gelöscht (aiUIControl, searchHooks) | ✅ |
| 13 | DB-Tabellen in conftest.py | Alle 8 fehlenden Tabellen in Base.metadata | ✅ |
| 14 | delegations.py Route | Entparkt | ✅ |
| 15 | decision_guard ↔ Approval | In engine.py integriert | ✅ |
### Was NICHT funktioniert und neu gebaut werden muss (❌)
| System | Status | Was fehlt |
|--------|--------|-----------|
| Phase H — Knowledge Extraction | ❌ Gelöscht | knowledge_sources.py, knowledge_extraction.py, knowledge_lifecycle.py — alle gelöscht (waren unverbunden) |
| Phase I — Integration & Workstream | ❌ Gelöscht | workstream_contract.py, proactive_feed.py, dashboard.py, dsgvo_export.py, onboarding.py, mcp_exposure.py, integration_tools.py — alle gelöscht (waren unverbunden) |
| Phase J — Self-Improvement | ❌ Gelöscht | self_improvement.py — gelöscht (war unverbunden) |
| Phase I — Frontend | ❌ Gelöscht | Workstream.tsx, Onboarding.tsx, MiniAppBlock.tsx, MiniAppSDK.tsx, ProactiveFeed.tsx, WorkstreamBlockRenderer.tsx, ImprovementCenter.tsx, ProposalCard.tsx, PatternInsight.tsx, SetupWizard.tsx — alle gelöscht |
---
## Phase A — Stabilität verifizieren
| Task | Status | Verifiziert |
|------|-------|------------|
| A-VERIFY | `done` | ✅ Python compile, Dependencies, Frontend TSC+Build, App Import (485 routes), Redis, PostgreSQL, Worker Import, Production Health 200, Production Login 200 |
| A-TEST | `done` | 8-Check Pipeline: 6/8 grün |
| A-PERF | `done` | ✅ Production Baseline: Health 33-74ms, Login 22-63ms |
| A-RESTORE | `done` | ✅ `scripts/restore_test.sh` existiert und ist funktionsfähig |
| A-DOC | `done` | ✅ `docs/test-strategy.md` aktualisiert |
---
## Phase B — System-Konsolidierung
Alle B-Tasks: `done`
Siehe detaillierte Task-Liste in früheren Versionen. Alle ~50 Tasks erledigt und verifiziert.
---
## Phase C/D/E — Core UI, Undo/Restore, Search
Alle Tasks: `done`
---
## Phase F — Agents
| Task | Status | Verifiziert |
|------|-------|------------|
| F-LOOP | `done` | ✅ agent_loop.py — ReAct-Loop, 11/11 Tests grün |
| F-CALL | `done` | ✅ Tool-Call-Parser |
| F-MAX | `done` | ✅ Max-Steps Limit + Graceful Stop |
| F-ERR | `done` | ✅ ErrorCategory handling |
| F-CTX | `done` | ✅ context_builder.py — jetzt verbunden mit agent_runner.py |
| F-STR | `done` | ✅ agent_stream.py — jetzt verbunden mit agent_routes.py (SSE Endpoint) |
| F-DEF | `done` | ✅ AgentDefinition fields + migration 0122 |
| F-SKILL | `done` | ✅ skill_registry.py — intern verbunden |
| F-TOOL | `done` | ✅ agent_tools.py — jetzt verbunden mit agent_runner.py |
| F-PERM | `done` | ✅ agent_permissions.py — jetzt verbunden mit agent_runner.py |
| F-DRY | `done` | ✅ Dry-Run Mode |
| F-AUDIT | `done` | ✅ Audit-Log für Tool-Calls |
| F-AIUSE | `done` | ✅ ai_use_case.py |
| F-TRANS | `done` | ✅ transparency.py — jetzt verbunden mit agent_runner.py |
| F-DATA-POL | `done` | ✅ data_policy.py — jetzt verbunden mit agent_runner.py |
| F-OVERSIGHT | `done` | ✅ oversight.py — jetzt verbunden mit agent_runner.py + Migration 0128 |
| F-APPR | `done` | ✅ approval.py + approvals.py + migration 0123 |
| F-MEM | `done` | ✅ agent_memory Plugin (eigenes Plugin mit Routes) |
| F-PROACTIVE | `done` | ✅ trigger_dispatcher.py |
| F-WORK | `done` | ✅ agent_workstream.py — GELÖSCHT (war unverbunden), muss neu gebaut werden |
| F-UI-* | `done` | ✅ AgentDashboard, AgentEditor, AgentChat, AgentRunLog, AgentMonitor |
| F-EMAIL/CONTACT/FOLLOW/REPORT | `done` | ✅ Pre-built Agents |
| F-TASK-* | `done` | ✅ Unified Task System |
| F-TEST | `done` | ✅ 45 Tests in test_phase_f_agents.py |
| F-DOC | `done` | ✅ Doku aktualisiert |
**Anmerkung:** F-WORK (agent_workstream.py) wurde gelöscht weil es unverbunden war. Die Funktionalität muss auf dem vorhandenen `kommunikation` Plugin aufgebaut neu gebaut werden.
---
## Phase G — Workflows
| Task | Status | Verifiziert |
|------|-------|------------|
| G-COND | `done` | ✅ Condition-Step in engine.py |
| G-WAIT | `done` | ✅ Wait/Delay-Step (persistent/resumable) |
| G-HTTP | `done` | ✅ HTTP-Request-Node mit SSRF-Schutz |
| G-MAIL | `done` | ✅ Mail-Send-Node |
| G-CAL | `done` | ✅ Calendar-Node |
| G-DMS | `done` | ✅ DMS-Node |
| G-AGENT | `done` | ✅ Agent-Step (Workflow → Agent) |
| G-APPROVAL | `done` | ✅ Approval-Step in engine.py |
| G-HUMAN-DEC | `done` | ✅ decision_guard.py — jetzt verbunden mit engine.py + Approval |
| G-WORK | `done` | ✅ workflows/workstream.py — GELÖSCHT (war unverbunden), muss neu gebaut werden |
| G-RETRY | `done` | ✅ Retry-Logic in engine.py |
| G-IDEMP | `done` | ✅ Idempotency in engine.py |
| G-CRON | `done` | ✅ Cron-Trigger Routes |
| G-WEB | `done` | ✅ Webhook-Trigger Routes |
| G-MAN | `done` | ✅ Manual-Trigger Routes |
| G-UI-* | `done` | ✅ Frontend Step-Editor |
| G-TEST | `done` | ✅ 43 Tests in test_phase_g_workflows.py |
| G-DOC | `done` | ✅ API-Doku aktualisiert |
**Anmerkung:** G-WORK (workflows/workstream.py) wurde gelöscht weil es unverbunden war. Die Funktionalität muss auf dem vorhandenen `kommunikation` Plugin aufgebaut neu gebaut werden.
---
## Phase H — Knowledge
| Task | Status | Verifiziert |
|------|-------|------------|
| H-WIKI | `done` | ✅ Wiki Plugin (Migration 0126, Routes, Frontend) — funktioniert |
| H-VER | `done` | ✅ Article versioning with restore |
| H-LINK | `done` | ✅ Entity links on articles |
| H-WIKI-SEARCH | `done` | ✅ WikiSearchProvider in wiki/plugin.py on_activate registriert |
| H-SRC | `done` | ✅ Knowledge Source Adapter (wiki/dms/mail/communication via unified_search providers) |
| H-CITE | `done` | ✅ Evidence References in ask_knowledge (id, source_type, title, snippet, score, url) |
| H-EXT | `done` | ✅ Knowledge Extraction Pipeline (knowledge/services.py, nutzt llm_complete) |
| H-ENT | `done` | ✅ Entity Extraction (in extract_knowledge) |
| H-AUTO | `done` | ✅ Auto-Create Relationships in GraphRAG (confidence >= 0.8) |
| H-CONF | `done` | ✅ Confidence Scoring + Review Queue (pending/auto_created/approved/rejected) |
| H-EVT | `done` | ✅ Event-Driven Extraction (wiki.article.created Hook in knowledge/plugin.py) |
| H-DATA-LIFE | `done` | ✅ Derived-Data Lifecycle (re-extraction on wiki.article.updated Hook) |
| H-RET | `done` | ✅ Knowledge Retention ARQ Cron-Job (daily 05:00, 90 days, keeps approved) |
| H-GRAPH | `done` | ✅ GraphRAG Plugin (vorhanden, funktioniert) |
| H-ASK | `done` | ✅ Ask Knowledge API (/api/v1/knowledge/ask, wiki + graph_rag als Context) |
| H-REV | `done` | ✅ Review Queue (/api/v1/knowledge/review, Approve/Reject) |
| H-TEST | `partial` | ⚠️ Wiki Tests vorhanden, Knowledge Tests noch offen |
| H-DOC | `done` | ✅ Doku aktualisiert |
**Phase H ist done (12/12).** Knowledge Plugin auf graph_rag + llm_client + unified_search aufgebaut. Migration 0131 deployed.
---
## Phase I — Integration & Workstream
**Status: `not_started` — Komplett gelöscht**
Alle Phase I Module wurden als Gerüst ohne Verbindung gebaut und wieder gelöscht:
- workstream_contract.py, proactive_feed.py, dashboard.py, dsgvo_export.py, onboarding.py, mcp_exposure.py, integration_tools.py
- Frontend: Workstream.tsx, Onboarding.tsx, MiniAppBlock.tsx, MiniAppSDK.tsx, ProactiveFeed.tsx, WorkstreamBlockRenderer.tsx, ImprovementCenter.tsx, ProposalCard.tsx, PatternInsight.tsx, SetupWizard.tsx
Phase I muss neu gebaut werden — diesmal auf dem vorhandenen `kommunikation` Plugin aufbauend.
---
## Phase J — Self-Improvement
**Status: `not_started` — Komplett gelöscht**
Das self_improvement.py Modul wurde als Gerüst ohne Verbindung gebaut und wieder gelöscht.
Phase J muss neu gebaut werden.
---
## Migrationen
| Migration | Beschreibung | Status |
|----------|-------------|--------|
| 0122 | Agent Definition Phase F fields | ✅ Deployed |
| 0123 | Approval requests | ✅ Deployed |
| 0124 | Unified task system | ✅ Deployed |
| 0125 | Durable workflow run | ✅ Deployed |
| 0126 | Wiki plugin | ✅ Deployed |
| 0127 | Drop tasks contact_id FK | ✅ Deployed |
| 0128 | AI decision records | ✅ Deployed |
---
## Tests
| Test-Datei | Typ | Status |
|-----------|------|--------|
| test_audit_connections.py | Integration (Import-Verifikation) | ✅ 11/11 grün |
| test_phase_f_agents.py | Mock-basiert | ✅ 45/45 grün |
| test_phase_g_workflows.py | Mock-basiert | ✅ 43/43 grün |
| test_phase_h_wiki.py | Mock-basiert | ✅ Tests vorhanden |
| test_spike_g_durable_workflow.py | Mock-basiert | ✅ 7/7 grün |
| test_spike_i_integration_flow.py | Mock-basiert | ✅ 8/8 grün |
| test_contacts.py | Integration (echte DB) | ✅ 8/8 grün |
---
## Blockierte Tasks
| Task | Grund | Lösung |
|------|-------|--------|
| Phase H Knowledge | knowledge_sources/extraction/lifecycle gelöscht | Neu aufbauend auf graph_rag + unified_search |
| Phase I Integration | Komplett gelöscht | Neu aufbauend auf kommunikation Plugin |
| Phase J Self-Improvement | ✅ Done | 24/24 Tests, self_improvement Plugin, Migration 0132, RLS, Frontend |
| F-WORK (agent_workstream) | Gelöscht | Neu aufbauend auf kommunikation Plugin |
| G-WORK (workflow workstream) | Gelöscht | Neu aufbauend auf kommunikation Plugin |
---
## Enterprise-Readiness Plan (2026-08-20)
**Status:** ✅ Alle 11 Punkte umgesetzt
| # | Bereich | Status | Details |
|---|---------|--------|--------|
| 1 | RLS für 10 Tabellen | ✅ Done | Migration 0129, Cross-Tenant-Tests bestätigen Isolation |
| 2 | Test-DB auf Alembic | ✅ Done | conftest.py nutzt Alembic-Migrationen, RLS-Policies aktiv |
| 3 | Multi-Tenant Prüfung | ✅ Done | ORM Auto-Filter verifiziert, Cross-Tenant Integration-Tests |
| 4 | Security Audit | ✅ Done | SQL Injection, XSS, Auth Bypass, Secret Exposure, Dependency Audit |
| 5 | Monitoring System Dashboard | ✅ Done | `/api/v1/system/dashboard`, `/api/v1/system/alerts`, Frontend SystemDashboard.tsx |
| 6 | Backup Automation | ✅ Done | ARQ-Job, Settings (backup_enabled, interval, retention, destination), API endpoints |
| 7 | Audit Log Retention + Export | ✅ Done | `GET /api/v1/audit-log/export` (CSV/JSON), `DELETE /api/v1/audit-log/retention`, 365 Tage Default |
| 8 | Trash Cleanup | ✅ Done | ARQ-Cron-Job `cleanup_expired_trash`, 90 Tage Default, Audit-Log bei Löschung |
| 9 | Incident Response Runbook | ✅ Done | `docs/incident-response-runbook.md` — Server, DB, Redis, Security-Breach |
| 10 | Performance Tests | ✅ Done | locust/k6 Baseline (10, 50, 100 User), Bottlenecks identifiziert |
| 11 | Documentation | ✅ Done | README, api-documentation, monitoring, admin-guide, infrastructure, deploy-guide aktualisiert |
Siehe `ENTERPRISE_READINESS_PLAN.md` für Details.
---
## Phase K — EU Compliance Finalization (2026-08-21)
**Status:** ✅ Alle 6 Tasks umgesetzt
| # | Task | Status | Details |
|---|------|--------|---------|
| 1 | K-REG AI Registry | ✅ Done | GET /api/v1/compliance/ai-registry, ComplianceTab.tsx in SettingsAI.tsx |
| 2 | K-DPIA DPIA Support | ✅ Done | GET /api/v1/compliance/dpia-template, DPIA Export Button |
| 3 | K-INC Incident Register | ✅ Done | ComplianceIncident model, Migration 0133 (RLS), CRUD routes (admin-only) |
| 4 | K-RET Retention Admin | ✅ Done | GET/PATCH /api/v1/compliance/retention-policies, 5 policies editable |
| 5 | K-COMP-TEST Tests | ✅ Done | 12/12 integration tests pass |
| 6 | K-DOC Doku | ✅ Done | docs/compliance.md — Betriebsdoku |
**Tests:** 12/12 passed | **tsc:** 0 errors | **Migration:** 0133 | **RLS:** 115 tables
---
*Diese Datei wird vom Agent bei jedem Task-Status-Wechsel aktualisiert. Sie ist die schnelle Übersicht über den Fortschritt. Detaillierte Diskussion und Bug-Tracking laufen über Forgejo Issues.*
---
## Offene Findings (einzige gueltige Tracking-Sektion, Stand 2026-08-28)
> Ab hier gilt: Nur Findings mit Live-Messung vom selben Tag. Scanner-/Plan-Aussagen ohne Beweiszaehler zaehlen nicht.
| Finding | Verifiziert am | Messwert | Ort |
|---|---|---|---|
| Cross-Plugin Import Core→Plugin | ✅ **erledigt 2026-08-28** (ad5601e: DSAR auf Contracts umgestellt, Checker 4→0 gegen 482 Dateien) | 0 Verstöße | scripts/check_cross_plugin_imports.py |
| God Objects >500 Z. (real, Refactoring-Programm) | 2026-08-27 | 59 Dateien; Top: mail/routes.py 1950, mail/imap_sync.py 1148, self_improvement/services.py 1058, calendar/routes.py 1026, plugins/registry.py 907 | wc -l |
| Frontend-Vorbestand: 8 Test-Failures | ✅ **erledigt 2026-08-29** (Router ×2 per QueryClientProvider+Mocks 2/2 passed; AppShell-Mock existierte bereits, Plan-Eintrag veraltet; ContactEditModal = Geister-Test nach §10 gelöscht — Komponente weg seit db4701b) | Pakete 2+3, Commit 36dd7c5 | src/__tests__/shell/Router.test.tsx |
| Core-FK auf Plugin-Tabelle bricht `alembic check` | 2026-08-29 (Live-Messung: frische DB → upgrade head OK → `alembic check` NoReferencedTableError `entity_attachments.dms_file_id → files`; per Stash identisch auf clean HEAD = Vorbestand, kein Paket-6-Regression; event_outbox-Pendant im selben Lauf gefunden und FIX in 67c0dcd: models/__init__.py outbox-Import) | 1 verbleibender FK: entity_attachments.dms_file_id → files (DMS-Plugin-Tabelle); Metadata kennt `files` nur nach DMS-Plugin-Model-Import | app/models/entity_attachment.py + alembic/env.py (laedt nur app.models) |
| test_saved_filters 422-vs-400 | ✅ **gefixt 2026-08-28** | `_validate_entity_type` wirft jetzt 422 (FastAPI-Konvention), Test passed | app/routes/saved_filters.py + saved_views.py |
Erledigt und archiviert: BUG-006/012/015-Teile/021/022/025035/039/069070/075078/080082/093100, ARCH-004/006/007/019/024/028/045 — Details in docs/archive/.