From 36a3c846991fa81494b1062273fc5b78accc227d Mon Sep 17 00:00:00 2001 From: Jens Date: Sun, 23 Aug 2026 23:31:42 +0200 Subject: [PATCH] Fix governed runtime model registration --- ...8230001_configure_model_source_registry.py | 43 +++++++++++++++++++ .../app/services/source_registry_service.py | 2 + ...test_runtime_model_provenance_migration.py | 28 ++++++++++++ scripts/migrate_runtime_model_provenance.py | 9 ---- 4 files changed, 73 insertions(+), 9 deletions(-) create mode 100644 backend/alembic/versions/202608230001_configure_model_source_registry.py diff --git a/backend/alembic/versions/202608230001_configure_model_source_registry.py b/backend/alembic/versions/202608230001_configure_model_source_registry.py new file mode 100644 index 00000000..215ad85d --- /dev/null +++ b/backend/alembic/versions/202608230001_configure_model_source_registry.py @@ -0,0 +1,43 @@ +"""Configure the immutable model source registry for governed snapshots. + +The phase-2 seed intentionally registered model artifacts as unknown. Runtime +model provenance now records exact immutable snapshots, so the server-owned +registry must advertise that configured capability. The write guard is only +disabled for this narrowly-scoped, versioned migration and is restored in the +same transaction. +""" + +from alembic import op + + +revision = "202608230001" +down_revision = "202608010001" +branch_labels = None +depends_on = None + + +def _set_status(*, freshness_status: str, ingest_status: str) -> None: + op.execute("ALTER TABLE source_registry DISABLE TRIGGER trg_source_registry_write_guard") + op.execute( + f""" + UPDATE source_registry + SET freshness_status = '{freshness_status}', + ingest_status = '{ingest_status}', + registry_metadata_json = ( + registry_metadata_json::jsonb || + '{{"runtime_model_contract": {{"key": "geointel.model.pytorch", "version": "1.0.0"}}}}'::jsonb + )::json, + updated_at = now() + WHERE source_key = 'model' + AND registry_metadata_json ->> 'registry_owner' = 'server' + """ + ) + op.execute("ALTER TABLE source_registry ENABLE TRIGGER trg_source_registry_write_guard") + + +def upgrade() -> None: + _set_status(freshness_status="current", ingest_status="configured") + + +def downgrade() -> None: + _set_status(freshness_status="unknown", ingest_status="registered") diff --git a/backend/app/services/source_registry_service.py b/backend/app/services/source_registry_service.py index ac05ec60..12c7fc90 100644 --- a/backend/app/services/source_registry_service.py +++ b/backend/app/services/source_registry_service.py @@ -389,6 +389,8 @@ SERVER_OWNED_SOURCE_DEFINITIONS: dict[str, SourceRegistryDefinition] = { "experimental", "GeoIntel model pipeline", {"scope": "internal_model_artifact"}, + freshness_status="current", + ingest_status="configured", known_limitations=( "A model artifact is not a validated capability or promotion decision without its model card and evaluation evidence.", ), diff --git a/backend/tests/test_runtime_model_provenance_migration.py b/backend/tests/test_runtime_model_provenance_migration.py index a2028e10..cc970650 100644 --- a/backend/tests/test_runtime_model_provenance_migration.py +++ b/backend/tests/test_runtime_model_provenance_migration.py @@ -135,3 +135,31 @@ def test_generated_sidecar_passes_exact_runtime_contract(tmp_path: Path) -> None assert validated.model_sha256 == evidence["checksums"]["model"] assert validated.runtime_manifest_sha256 == payload["metadata"]["runtime_manifest_sha256"] assert module._write_manifest_atomically(manifest_path, payload) is False + + +def test_model_registry_definition_is_runtime_ready_without_mutating_server_owned_row() -> None: + from app.services.source_registry_service import SourceRegistryService + + definition = SourceRegistryService.definition_for("model") + + assert definition.ingest_status == "configured" + assert definition.freshness_status == "current" + script = SCRIPT.read_text(encoding="utf-8") + assert 'source.ingest_status = "configured"' not in script + assert 'source.freshness_status = "current"' not in script + + +def test_model_registry_status_migration_is_narrow_and_restores_write_guard() -> None: + migration = ( + ROOT + / "backend" + / "alembic" + / "versions" + / "202608230001_configure_model_source_registry.py" + ).read_text(encoding="utf-8") + + assert "WHERE source_key = 'model'" in migration + assert "registry_owner' = 'server'" in migration + assert migration.count("DISABLE TRIGGER trg_source_registry_write_guard") == 1 + assert migration.count("ENABLE TRIGGER trg_source_registry_write_guard") == 1 + assert 'down_revision = "202608010001"' in migration diff --git a/scripts/migrate_runtime_model_provenance.py b/scripts/migrate_runtime_model_provenance.py index 8c1659da..a4571acf 100644 --- a/scripts/migrate_runtime_model_provenance.py +++ b/scripts/migrate_runtime_model_provenance.py @@ -243,15 +243,6 @@ def migrate(args: argparse.Namespace) -> tuple[int, dict[str, Any]]: created_manifest = False try: source = SourceRegistryService.ensure_server_owned_source(db, RuntimeModelProvenanceService.SOURCE_REGISTRY_KEY) - source.ingest_status = "configured" - source.freshness_status = "current" - registry_metadata = dict(source.registry_metadata_json or {}) - registry_metadata["runtime_model_contract"] = { - "key": PYTORCH_MODEL_CONTRACT_KEY, - "version": PYTORCH_MODEL_CONTRACT_VERSION, - "claim_boundary": CLAIM_BOUNDARY, - } - source.registry_metadata_json = registry_metadata snapshot = SourceRegistryService.record_snapshot( db,