diff --git a/PROGRESS.md b/PROGRESS.md index f287bb6..ac38af8 100644 --- a/PROGRESS.md +++ b/PROGRESS.md @@ -65,6 +65,12 @@ | 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 | — | **Block A ABGESCHLOSSEN** — Gate A bestanden (32f63ad). **Block B ABGESCHLOSSEN** — Gate B bestanden (alle 5 Checks bewiesen). @@ -72,7 +78,8 @@ **Block D GESTARTET** — D2 komplett (d89044d). Offen: D1 (pytest-Massen), D3 (API-/Testpfade + ARCH-051/055/056/057), D4 (Security-Triage BUG-019/020 + ARCH-027), D5 (Marathon-Triage), D6 (Legacy-Migration ARCH-059/023). ### Bekannte Vorbestände (Block D Triage) -- 9 Contact/Company-Test-Failures existieren seit vor Block B (Stash-Verifikation 2026-08-23): KeyError 'id' bei Create/Delete/Link-Flows — Ursache ungeklärt, gehört zur pytest-Massen-Triage. +- ~~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: 28 Failures 'ValueError: Unknown entity type: mail_account' aus permission_resolver._get_entity_model — Stash-verifiziert als Vorbestand (2026-08-24); Permission-Resolver-Mapping kennt mail_account nicht → D3/D4-Thema. - 5 PluginLoader-Test-Failures sind Vorbestand (Stash-Verifikation): Tests erwarten UI-Text 'Failed to load plugin', aktueller Loader zeigt deutsche Texte. - Geister-Komponenten: Backend-Manifeste referenzieren @/pages/AIAssistant + 5 Contact-Detail-Tabs, die im Frontend nicht existieren (siehe unten). diff --git a/alembic/versions/0142_add_backup_config_columns.py b/alembic/versions/0142_add_backup_config_columns.py new file mode 100644 index 0000000..b2ab5be --- /dev/null +++ b/alembic/versions/0142_add_backup_config_columns.py @@ -0,0 +1,39 @@ +"""Add backup config columns to system_settings table. + +Follow-up to 0130: the backup feature (10b1f83) added backup_interval, +backup_retention_days and backup_destination to schema/service/frontend +but missed model columns and this migration. + +Revision ID: 0142 +Revises: 0141 +""" + +import sqlalchemy as sa + +from alembic import op + +revision = "0142" +down_revision = "0141" +branch_labels = None +depends_on = None + + +def upgrade() -> None: + op.add_column( + "system_settings", + sa.Column("backup_interval", sa.String(20), nullable=False, server_default="daily"), + ) + op.add_column( + "system_settings", + sa.Column("backup_retention_days", sa.Integer(), nullable=False, server_default="7"), + ) + op.add_column( + "system_settings", + sa.Column("backup_destination", sa.String(20), nullable=False, server_default="local"), + ) + + +def downgrade() -> None: + op.drop_column("system_settings", "backup_destination") + op.drop_column("system_settings", "backup_retention_days") + op.drop_column("system_settings", "backup_interval") diff --git a/app/models/system_settings.py b/app/models/system_settings.py index b5aed94..8bf570a 100644 --- a/app/models/system_settings.py +++ b/app/models/system_settings.py @@ -53,6 +53,9 @@ class SystemSettings(Base, TenantMixin, OwnedMixin): theme_border_radius: Mapped[str] = mapped_column(String(20), nullable=False, default="0.5rem") # Backup configuration backup_enabled: Mapped[bool] = mapped_column(Boolean, nullable=False, default=False, server_default="false") + backup_interval: Mapped[str] = mapped_column(String(20), nullable=False, default="daily", server_default="daily") + backup_retention_days: Mapped[int] = mapped_column(Integer, nullable=False, default=7, server_default="7") + backup_destination: Mapped[str] = mapped_column(String(20), nullable=False, default="local", server_default="local") # Automation plugin settings (JSONB) automation_config: Mapped[dict | None] = mapped_column(JSONB, nullable=True) # Retention policy overrides (JSONB) — compliance module