Files
leocrm/SCHEMA_DRIFTS.md
T
Agent Zero a614ab337b
Check Cross-Plugin Imports / check (push) Has been cancelled
fix: schema drifts, RLS policies, wiki plugin, agent_loop syntax, test imports, frontend error handling
- Migration 0135: Fix 3 VARCHAR length drifts + 2 missing tables (forgejo_reported_errors, pgp_keys)
- Migration 0136: Fix 8 RLS policies referencing app.tenant_id instead of app.current_tenant_id
- wiki/__init__.py: Import WikiPlugin for discover_builtins()
- wiki/plugin.py: Fix SyntaxError (unterminated triple-quoted string)
- agent_loop.py: Fix SyntaxError (stray n character in dict)
- test_p1_6_dms_streaming.py: Fix import (CHUNK_SIZE removed, use _sanitize_filename only)
- conftest.py: Use create_all only (alembic conflicts with create_all in tests)
- frontend errorTypes.ts: asError() now handles nested detail objects
- AGENTS.md: Sub-agents forbidden in this project
- DAMAGE_REPORT.md + SCHEMA_DRIFTS.md: Complete damage assessment
- scripts/schema_drift_check.py: Schema drift checker tool

Tests: 24/24 Phase J + 12/12 Phase K = 36/36 passed
tsc: 0 errors
Frontend build: successful
2026-08-21 10:02:50 +02:00

7.6 KiB

Schema Drift Analysis — LeoCRM

Date: 2026-08-21
Method: scripts/schema_drift_check.py executed in production container crm_app against crm_db
Total Drifts: 111

Summary

Issue Type Count Action
VARCHAR LENGTH MISMATCH 3 Migration 0135: ALTER COLUMN TYPE
TABLE MISSING IN DB 2 Migration 0135: CREATE TABLE
COLUMN IN DB NOT IN MODEL 91 Models need updating (columns already in DB via migrations)
TABLE IN DB NOT IN MODEL 15 False positives (models exist but not loaded by drift script)

1. VARCHAR LENGTH MISMATCH (3)

These are the most critical drifts — the model defines a longer VARCHAR than the DB column, meaning writes can fail in production.

Table Column Model Type DB Type Fix
contacts status VARCHAR(30) VARCHAR(20) ALTER COLUMN TYPE VARCHAR(30)
notifications type VARCHAR(100) VARCHAR(20) ALTER COLUMN TYPE VARCHAR(100)
notification_preferences type_key VARCHAR(100) VARCHAR(20) ALTER COLUMN TYPE VARCHAR(100)

2. TABLE MISSING IN DB (2)

Models define these tables but they don't exist in the production database.

Table Model Location Fix
forgejo_reported_errors app/plugins/builtins/forgejo_error_reporter/models.py CREATE TABLE in migration 0135
pgp_keys app/plugins/builtins/mail/models.py CREATE TABLE in migration 0135

3. COLUMN IN DB NOT IN MODEL (91)

These columns exist in the production database (added by Alembic migrations) but are NOT defined in the SQLAlchemy models. This means Base.metadata.create_all() (used by tests) creates tables WITHOUT these columns, while production has them.

3.1 owner_id Columns (78 tables)

The OwnedMixin adds an owner_id column. Many models don't use OwnedMixin but migrations added owner_id to their tables.

Table Column DB Type
ai_conversations owner_id uuid
ai_messages owner_id uuid
audit_log owner_id uuid
password_reset_tokens owner_id uuid
api_tokens owner_id uuid
backups owner_id uuid
contactpersons owner_id uuid
contact_merge_history owner_id uuid
currencies owner_id uuid
entity_permissions owner_id uuid
entity_policies owner_id uuid
groups owner_id uuid
user_groups owner_id uuid
notifications owner_id uuid
notification_preferences owner_id uuid
permission_delegations owner_id uuid
permission_templates owner_id uuid
roles owner_id uuid
sessions owner_id uuid
system_settings owner_id uuid
tax_rates owner_id uuid
user_tenants owner_id uuid
workflow_instances owner_id uuid
workflow_step_history owner_id uuid
workspace_modules owner_id uuid
workspace_users owner_id uuid
workspace_widgets owner_id uuid
ai_providers owner_id uuid
ai_models owner_id uuid
ai_presets owner_id uuid
ai_chat_messages owner_id uuid
ai_chat_folders owner_id uuid
ai_chat_attachments owner_id uuid
ai_proactive_context_log owner_id uuid
ai_proactive_settings owner_id uuid
automation_agent_versions owner_id uuid
automation_versions owner_id uuid
automation_cron_jobs owner_id uuid
automation_agent_runs owner_id uuid
automation_runs owner_id uuid
agent_subtasks owner_id uuid
calendar_entry_links owner_id uuid
calendar_shares owner_id uuid
user_calendar_visibility owner_id uuid
resources owner_id uuid
resource_bookings owner_id uuid
comm_participants owner_id uuid
comm_messages owner_id uuid
comm_message_blocks owner_id uuid
comm_message_attachments owner_id uuid
comm_message_reactions owner_id uuid
comm_message_reads owner_id uuid
comm_conversation_pins owner_id uuid
comm_conversation_mutes owner_id uuid
comm_message_edits owner_id uuid
mail_folders owner_id uuid
mail_attachments owner_id uuid
mail_labels owner_id uuid
mail_label_assignments owner_id uuid
mail_rules owner_id uuid
mail_templates owner_id uuid
mail_signatures owner_id uuid
vacation_sent_log owner_id uuid
mail_seen_by owner_id uuid
mail_account_delegates owner_id uuid
mail_account_send_permissions owner_id uuid
contact_pgp_keys owner_id uuid
mail_sync_queue owner_id uuid
permissions owner_id uuid
tag_assignments owner_id uuid
unified_search_providers owner_id uuid
unified_search_index_log owner_id uuid

3.2 Search/Embedding/Index Columns (13)

Table Column DB Type Purpose
audit_log search_tsv tsvector Full-text search
contacts indexed_at timestamp with time zone Unified search index timestamp
calendar_entries search_tsv tsvector Full-text search
calendar_entries embedding USER-DEFINED (vector) Vector embedding
calendar_entries indexed_at timestamp with time zone Search index timestamp
files embedding USER-DEFINED (vector) Vector embedding
files content_text text Extracted text content
files content_tsv tsvector Full-text search
files indexed_at timestamp with time zone Search index timestamp
mails company_id uuid Company reference
mails body_tsv tsvector Full-text search
mails embedding USER-DEFINED (vector) Vector embedding
mails indexed_at timestamp with time zone Search index timestamp
tags search_tsv tsvector Full-text search
tags embedding USER-DEFINED (vector) Vector embedding
agent_memories embedding USER-DEFINED (vector) Vector embedding
user_groups deleted_at timestamp with time zone Soft delete
notification_types deleted_at timestamp with time zone Soft delete

4. TABLE IN DB NOT IN MODEL (15) — False Positives

These tables have models but the drift script doesn't import all model modules. They are NOT real drifts.

Table Model Location
ai_decision_records app/ai/oversight.py (DecisionRecordDB)
approval_requests app/core/approval.py
companies_old Legacy table (deprecated)
company_contacts_old Legacy table (deprecated)
contacts_old Legacy table (deprecated)
event_outbox app/models/outbox.py (EventOutbox)
notifications_legacy Legacy table (deprecated)
outbox_deliveries app/models/outbox_delivery.py (OutboxDelivery)
plugin_allowlist app/models/plugin_allowlist.py (PluginAllowlist)
saved_filters app/models/saved_filter.py (SavedFilter)
tenant_plugin_activation Plugin activation table
user_preferences app/models/user_preference.py (UserPreference)
wiki_article_versions app/plugins/builtins/wiki/models.py
wiki_articles app/plugins/builtins/wiki/models.py
wiki_categories app/plugins/builtins/wiki/models.py

Root Cause

  • Tests use Base.metadata.create_all() which creates tables from SQLAlchemy model definitions
  • Production uses Alembic migrations which may add columns not in models (e.g., owner_id, search_tsv, embedding)
  • This causes schema drift: tests pass but production may fail on missing columns or wrong VARCHAR lengths

Fix

  1. Migration 0135: Fix VARCHAR lengths + create missing tables
  2. conftest.py: Switch from Base.metadata.create_all() to alembic upgrade head so tests use the same schema as production
  3. Models: Should be updated to include OwnedMixin and search/embedding columns (separate task)