- **Phase O** — UI-Overhaul (umbenannt von Doppel-L, Bug-Verifikation steht im Roadmap-Eintrag: 5/7 Bugs bereits erledigt, offen: 1.2 Kontakte-Drag-Drop in Ordner, 1.3 MoveDialog)
**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').
| 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` |
## 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
-`app/plugins/miniapp_registry.py` (154 Z.): Registry in den Plugin-Layer gehoben (Plattform-Konzept). MiniAppDef erweitert um `permission` (fail-closed, leer = jeder), `settings_schema`, `col_span`/`row_span`, `hosts` (chat/dashboard/window), `component`, `order`, `builtin`.
- Kompatibilitäts-Brücke: `kommunikation/miniapp_registry.py` re-exportiert die Universal-Registry — alle Bestands-Importer (kommunikation contracts, automation routes, tests) unverändert lauffähig.
- Lifecycle: `BasePlugin.on_activate` registriert Manifest-Beiträge automatisch (miniapps + dashboard_widgets-Alias mit component/spans/permission — ein Contribution-Typ, #359-Philosophie); `on_deactivate` entfernt per `unregister_plugin` nur die eigenen Apps.
## Phase N — Workspace-Scopes (2026-08-30 geplant, user-abgestimmt)
**User-Vision:** Workspaces als voll anpassbare Arbeitskontexte — jedes Modul pro Workspace auf Teilmengen einschränkbar (z.B. nur Kontakt-Ordner X+Y, nur DMS-Ordner "Angebote", nur Mail-Postfach vertrieb@, nur Kalender "Vertrieb"). Admin-definiert, für zugewiesene User-Gruppen.
**WICHTIG — Klarstellung Workspace ≠ Dashboard (user-korrigiert):** Zwei getrennte Systeme. Workspace = Admin-Kontext (WAS ist sichtbar/verfügbar, Gruppen-Feature). Dashboard = persönlich (WIE ICH mein Dashboard baue, Phase M). workspace_widgets bleibt Workspace-Eigentum (verfügbare Widget-TYPEN), dashboards-Tabelle (Phase M2) bleibt User-Eigentum (persönliches Layout). Kein Überbau, keine Vermischung.
**Status:** not_started — Phase N (N1-N4) in PLATFORM_ROADMAP.md verankert. 0 Umbau nötig: Speicher (workspace_modules.config JSONB), Transport (X-Workspace-ID-Interceptor), Context-Endpoint und Sidebar-Consumer existieren bereits; N3/N4 = additive Scope-Anwendung in Modul-Listen (kein Refactoring).
**Security-Invariante:** Scope = reine UND-Einschränkung (Workspace-Scope ∧ RLS ∧ ABAC ∧ Permissions). Workspace kann NIE mehr sichtbar machen, nur weniger. Ohne Workspace = kein Filter (rückwärtskompatibel).
**User-Entscheidung:** Wiki wird komplett ersetzt durch Notion-artige Notizen-/Firmen-Wissen-App. Keine Legacy-App, keine Notion-Datenbanken erstmal — MiniApp-Blöcke stattdessen. Quer-Verweise + vollständige Such-Indexierung Pflicht. Edit-Konzept: Live-Inline-Editing wie Notion (kein Mode-Toggle, Auto-Save), Lese-Modus entsteht über Permissions + optional Page-Lock.
**Status:** not_started — Phase P (P1-P5) in PLATFORM_ROADMAP.md verankert. P1-P3+P5 unabhängig startbar; P4 braucht M1 (MiniApp-Registry).
**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.
**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).
**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.
| Doppelquelle: statische `contact/contacts/company`-Einträge in `ENTITY_MODELS` neben identischer dynamischer Lieferung via `ContactsPlugin.get_entity_models()` (Kritikpunkte 9–11) | [#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 |
| 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 |
| 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 |
| 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 |
| 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 |
| 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-027–029/031–035/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 | — |
| 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-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 |
| 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 |
| I-A | Stale-Status: 13 bereits gefixte Findings ohne ✅ in test-bugs.md (ARCH-051/055/056/057/027, BUG-085–092) | ✅ Nachdokumentiert mit Beweis-Commit-Referenzen | b9a6c06 |
| 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 |
| 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 |
- ~~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.
**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.
**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.
**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.
*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.*
| 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) |
Erledigt und archiviert: BUG-006/012/015-Teile/021/022/025–035/039/069–070/075–078/080–082/093–100, ARCH-004/006/007/019/024/028/045 — Details in docs/archive/.