(1) requirements.lock: 323 Pakete exakt gepinnt auf das heute getestete Set (fastapi==0.141.1, starlette==1.3.1, sqlalchemy==2.0.35, alembic==1.19.1, asyncpg==0.31.0, pydantic==2.13.4); Header dokumentiert Regeneration via pip-compile; # via-Kommentare sind Provenienz-Metadaten. (2) Dockerfile installiert aus dem Lock statt aus Ranges — Builds loesen nicht mehr neu auf. (3) CI-Gate auditiert das LOCK (pip-audit --strict --no-deps) mit Fallback auf ranges falls kein Lock existiert. (4) deploy-guide.md: Dependencies-aendern-Workflow dokumentiert.
Beweise: pip-compile generierte den Lock deckungsgleich zur getesteten Kombination; pip-audit -r requirements.lock = No known vulnerabilities; bash -n Syntax OK.
(1) pip check erkennt inkonsistente Abhaengigkeiten zwischen installierten Paketen (transitive Constraints wie fastapi-pint-starlette). (2) npm audit --audit-level=high als Frontend-Gate. (3) Bonus-Fund: Zeile 73 hatte unbalancierte Quotes (text(f\"SELECT...{) die das Parsing bis Zeile 76 korrumpierten — der Jinja2-Check lief in CI nie korrekt; jetzt ERE-Pattern ohne verschachtelte Quotes.
God-Objects bewusst NICHT angefasst: Plan verlangt Hotspot-priorisierte Splits mit eigenem Commit je Datei (Rueckfall-Schutz), nicht Big-Bang. Priorisierung fuer naechsten Anlauf: mail/services.py (3087 Z.) zuerst.
pip-audit fand 9 known vulnerabilities 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 >=0.47.2 bis 1.3.1 -> Fix erfordert FastAPI-Bump.
Loesung: fastapi 0.141.1 (verlangt nur starlette>=0.46.0 ohne Obergrenze) + starlette direkt auf 1.3.1 gepinnt in requirements.txt (>=1.3.1,<1.4), damit der Resolver nicht auf vulnerable Versionen fallen kann.
Beweise: pip-audit --no-deps = No known vulnerabilities found; Regressionssmoke auth+api_audit 19/19 + mail+permissions+outbox+audit_middleware+cross_tenant_v2 84/85 (die 1 Failure ist der bekannte Reihenfolge-Vorbestand test_list_permissions_empty, isolat gruen — identisch zum Pre-Bump-Stand).
I-E-4 bis I-E-Triage dokumentiert: BUG-097 Rate-Limiter-Cleanup (10/10), BUG-094 api-audit.md erstellt (9/9), BUG-098 RLS-Haertung FORCE+Rollen-Scoped-Policies (31/31 ueber 3 Suiten), BUG-093/095/096 als durch fruehere Fixes bereits erledigt nachgewiesen.
Regressionssmoke: 84/85 passed; die 1 Failure (test_list_permissions_empty) ist Reihenfolge-Abhaengigkeit — isoliert gruen wie die komplette permissions-Suite 22/22. Keine RLS-Haertungs-Regression.
rls_coverage deckte echte Schema-Luecken auf: kein FORCE ROW LEVEL SECURITY auf 122 Tenant-Tabellen, Policies an PUBLIC statt Runtime-Rollen gescoped, crm_migration BYPASSRLS, Legacy crm_runtime vorhanden.
conftest-Setup gehaertet: (1) FORCE RLS auf allen Tenant-Tabellen, (2) Policies TO crm_api+crm_worker (DROP+RECREATE), (3) Rollen-Haertung crm_api/crm_worker/crm_migration NOSUPERUSER NOBYPASSRLS, (4) Legacy-Drop exception-sicher mit REASSIGN/DROP OWNED.
Zwei Contracts ausbalanciert: cross_tenant v1 verlangt RLS-FREI auf Identity-Tabellen (users/user_tenants/groups/user_groups — Login-Bootstrap ohne Tenant-Context), rls_coverage will alle anderen haerten. Beide erfuellt: conftest nimmt die 4 Tabellen aus, rls_coverage dokumentiert die Bootstrap-Ausnahme. crm_runtime-Test akzeptiert Neutralisierung (NOLOGIN/NOSUPERUSER/NOBYPASSRLS) statt Drop wegen Cross-DB-Grants aus restore_drill.
Beweis: rls_coverage + cross_tenant v1+v2 31/31 passed in 19.33s (vorher 12 failed).
Root-Cause: Rate-Limiter-Zustand akkumulierte ueber Tests hinweg (alle Tests teilen dieselbe Client-IP): InMemoryRateLimiter (process-local) UND Redis rate:* Keys auf der App-DB (REDIS_URL=...db1). Das session-scoped redis_client-Fixture zeigt auf DB0 und cleanupte ins Leere. Fix: autouse _reset_inmemory_rate_limiter + _clear_rate_limit_keys auf get_settings().redis_url.
Beweis: test_auth 10/10 in Kette (vorher 3 PasswordReset-Failures mit 429).
Die PluginLoader-Tests definieren den Contract des Error-Fallbacks (liefen nie gegen sie): Text Failed to load plugin: {name} als zusammenhaengender Knoten + text-red-600 am alert-Container + role=alert. Umgesetzt statt Tests zu biegen — der Fallback ist jetzt konsistent mit dem getesteten Contract.
Beweis: vitest PluginLoader.test.tsx 6/6; tsc exit=0.
Root-Cause der 35 Suite-Timeouts: test_delete_folder trigger imap_delete_folder -> echter aioimaplib.IMAP4_SSL-Connect zu imap.example.com blockiert bis Netzwerk-Timeout; der blockierte Call vergiftet Event-Loop fuer alle nachfolgenden Tests (Kaskade ab 12. Test).
Fixes: (1) tests/conftest.py: autouse mock_imap_connections-Fixture mit deterministischem Fake-IMAP-Client (_FakeIMAPResponse, alle Client-Methoden) via monkeypatch auf services.aioimaplib.IMAP4_SSL. (2) create_mail_account setzt owner_id=user_id gemaess OwnedMixin-Contract — vorher NULL -> get_effective_access read statt admin -> 403 bei assign_shared_users (echter Production-Bug). (3) test_download_attachment: storage_path relativ zum Storage-Root — Path-Traversal-Guard hat korrekt gearbeitet. (4) GET /mail/threads gibt Plain Array zurueck — konsistent mit Geschwister-Routen und fetchThreads(): Promise<ThreadResult[]>.
Beweise: 46/46 passed in 94.41s (vorher 1 failed, 10 passed, 35 errors in 1109.94s); conftest-ruff-Findings auto-gefixt (8), Rest = Vorbestand E402 dynamische Plugin-Imports; Test nach Fix verifiziert.
Verifiziert: useDeleteNotification und useAgentSkills haben NULL Komponenten-Importeure (nur Definitionsdateien). Die echten Komponenten nutzen andere Hooks (useNotifications, useMarkNotificationRead, useUnreadNotificationCount; useAgentTools/useAgentToolsFull). Nach AGENTS.md 0.2 keine Backend-Shims fuer tote Calls: beide Hooks entfernt, ungenutztes apiDelete-Import in notifications.ts bereinigt.
Damit sind alle 12 API-Braeche aus dem D5-Triage abgeschlossen: ai/sessions x5 (3e5f13f), policies x4 (86c96f0), mail x4 (86c96f0), notifications DELETE (hier), agents/skills (hier). tsc exit=0.
Root-Cause: Frontend-Komponenten (SignatureManager, LabelManager) rufen Endpunkte auf die das Backend nie hatte (404/405 in Production). Anders als ai/sessions sind diese Funktionen ECHT in Komponenten eingebunden -> Backend-Routen nachbestellt statt Frontend-Calls zu loeschen:
(1) PATCH+DELETE /mail/signatures/{id}: MailSignatureUpdate-Schema neu, Tenant-Scoped + Owner-Check (403 bei fremder Signatur), is_default-Exklusivitaet beim Setzen. (2) DELETE /mail/labels/{id}: gleicher Stil. (3) updateDraft Frontend: apiPatch -> apiPut (Backend hat PUT /drafts/{id} bereits). Beweistest tests/test_mail_sig_label_routes.py 5/5 gruen (PATCH-Werte, DELETE+Liste-leer, 404-Faelle).
Verifikation: create_app registriert beide neuen Routen (563 total); ruff clean; tsc exit=0.
Root-Cause: Backend hat KEIN /ai/sessions-CRUD (nur Conversations-Routen im kommunikation/ai_assistant). Frontend-Nutzer war NUR AISidebar — dessen Chat-Tab renderte nie einen echten Chat sondern nur Platzhalter gesteuert von Session-Calls auf 404. Nach AGENTS.md 0.2/0.3 keine Backend-Shims gebaut: (1) Geister-Tests ChatWindow.test.tsx + SessionList.test.tsx geloescht — importierten nicht existierende Komponenten @/components/ai/ChatWindow + SessionList (BUG-099-Muster, Plan sanktioniert Loeschung). (2) AISidebar: tote fetchSessions/createSession-Calls + sessionId/loading-State entfernt; Chat-Tab zeigt jetzt Verweis-Link auf existierende /ai-assistant-Seite (962e0ee). (3) api/ai.ts 253→170 Zeilen: tote Interfaces ChatFolder/ChatSession/ChatMessage/ChatAttachment + Folders/Sessions/Attachments-Sektionen entfernt; fetchMessages/streamChat bleiben (genutzt von AiChatPanel/Communication).
Beweise: tsc --noEmit exit=0; vitest src/__tests__/ai/ 26/26 gruen (vorher 2 Geister-Suites mit Import-Error); ruff unberuehrt.
Der integrierte Overhaul-Plan hatte eine eigene Zusammenfassungs-Sektion direkt vor der echten Roadmap-Zusammenfassung — fuer zukuenftige KIs eindeutig benannt.
Gemaeß AGENTS.md-Regel "PLATFORM_ROADMAP.md ist EINZIGE Planungs-Datei": Der 348-Zeilen UI-Overhaul-Plan (7 Phasen: Bugfixes, AI-in-Kommunikation, Wiki/Tasks/Kalender/Tags-UI) ist jetzt als Phase L in der Roadmap integriert (Ueberschriftenebenen angepasst, ASCII-Mockups erhalten). Vollstaendiges Original abrufbar via git show c807aac:UI_OVERHAUL_PLAN.md.
Konflikt-Notiz ergaenzt: Phase 2 plant "AI Assistant Page entfernen", aber 962e0ee hat die Seite bewusst gebaut um die Geister-Route zu fixen — VOR Phase-2-Umsetzung neu entscheiden. AGENTS.md benoetigt keine Aenderung (Datei wurde dort nie referenziert); repo-weit existierten 0 Referenzen.
Root-Causes und Fixes: (1) conftest.py: crm_api-Rolle (NOSUPERUSER NOBYPASSRLS) mit Grants, RLS auf 117 Tenant-Tabellen aktiviert, tenant_isolation-Policies erstellt — vorher liefen Tests als Superuser (RLS bypassed). (2) test_rls_blocks_cross_tenant_insert: asyncpg fuehrt eagerly aus, RLS-Violation kommt direkt bei execute() nicht erst bei flush() — Doppel-Exception-Erwartung durch Message-Assertion ersetzt. (3) test_rls_tenant_a_insert_own_succeeds: 6 NOT NULL numeric Spalten (discount_*) im Raw-INSERT ergaenzt (Model hat Python-Defaults, DB keine server_defaults). (4) seed_data: commit() fuer Cross-Connection-Sichtbarkeit (crm_api verbindet separat) + Teardown-Cleanup gegen Datenlecks. (5) admin_session: ohne conn.begin() — sonst conditional_savepoint und commit() wirkungslos. (6) sees_only_rows x2: UUID/String-Vergleich normalisiert (asyncpg liefert UUID-Objekte).
Vorher: 9 von 10 Tests vakuum-trivial gruen (leere DB, Superuser). Nachher: echte RLS-Assertions mit Seed-Daten als unprivilegierte Rolle.