feat(#357): Paket 6 — Contact-Model ins ContactsPlugin (physischer Move + PEP-562-Lazy-Re-Export-Bruecke, ALEMBIC_OWNED_TABLES gegen Schema-Dual-Ownership, outbox-Vorbestands-Fix in models/__init__)
Check Cross-Plugin Imports / check (push) Has been cancelled

This commit is contained in:
Agent Zero
2026-08-29 02:48:31 +02:00
parent df85fdcb5b
commit 67c0dcd34c
7 changed files with 429 additions and 253 deletions
+4
View File
@@ -64,6 +64,10 @@ EXEMPT_PATHS = {
PROJECT_ROOT / "app" / "plugins" / "base.py",
PROJECT_ROOT / "app" / "plugins" / "manifest.py",
PROJECT_ROOT / "app" / "plugins" / "migration_runner.py",
# Paket 6 (#357): deliberate re-export bridge — the ContactsPlugin owns the
# Contact model; app/models/contact.py only mirrors it so alembic env.py,
# Core services and tests keep their stable import path.
PROJECT_ROOT / "app" / "models" / "contact.py",
}
# Pattern for cross-plugin imports
+18 -3
View File
@@ -15,10 +15,10 @@ Environment:
from __future__ import annotations
import importlib
import logging
import os
import pkgutil
import sys
import logging
from typing import Any
# Ensure /app is in sys.path for container execution
@@ -33,6 +33,16 @@ from sqlalchemy.orm import DeclarativeBase
logger = logging.getLogger("sync_plugin_schema")
logging.basicConfig(level=logging.INFO, format="[sync] %(levelname)s: %(message)s")
# ── Alembic-owned tables (Paket 6, #357) ────────────────────────────────────
# Tables whose ORM model classes moved into a plugin package but whose schema
# is still owned by the Alembic migration chain (142 migrations reference
# them). The plugin schema sync must NOT manage these tables — dual ownership
# would cause schema drift between the two mechanisms.
ALEMBIC_OWNED_TABLES: set[str] = {
"contacts",
"contactpersons",
}
# ── Plugin model discovery ──────────────────────────────────────────────────
def _import_all_plugin_models() -> list[type[DeclarativeBase]]:
@@ -48,7 +58,7 @@ def _import_all_plugin_models() -> list[type[DeclarativeBase]]:
return models
# Import the builtins package to trigger model registrations
for importer, modname, ispkg in pkgutil.iter_modules(builtins_pkg.__path__):
for _importer, modname, ispkg in pkgutil.iter_modules(builtins_pkg.__path__):
if not ispkg:
continue
module_path = f"app.plugins.builtins.{modname}.models"
@@ -66,7 +76,12 @@ def _import_all_plugin_models() -> list[type[DeclarativeBase]]:
# Filter to only plugin tables — we identify them by checking if the table
# name appears in any plugin models module. We use metadata.tables which
# contains all registered tables.
for table_name, table in Base.metadata.tables.items():
for table_name, _table in Base.metadata.tables.items():
# Alembic-owned tables never go through the plugin sync, even when
# their model class lives in a plugin package (Paket 6: contacts,
# contactpersons — 142 Alembic migrations own their schema).
if table_name in ALEMBIC_OWNED_TABLES:
continue
# We only process tables that are defined in plugin modules.
# Core models are handled by alembic migrations.
# We check the module of the model class.