Fix governed runtime model registration
This commit is contained in:
@@ -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")
|
||||||
@@ -389,6 +389,8 @@ SERVER_OWNED_SOURCE_DEFINITIONS: dict[str, SourceRegistryDefinition] = {
|
|||||||
"experimental",
|
"experimental",
|
||||||
"GeoIntel model pipeline",
|
"GeoIntel model pipeline",
|
||||||
{"scope": "internal_model_artifact"},
|
{"scope": "internal_model_artifact"},
|
||||||
|
freshness_status="current",
|
||||||
|
ingest_status="configured",
|
||||||
known_limitations=(
|
known_limitations=(
|
||||||
"A model artifact is not a validated capability or promotion decision without its model card and evaluation evidence.",
|
"A model artifact is not a validated capability or promotion decision without its model card and evaluation evidence.",
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -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.model_sha256 == evidence["checksums"]["model"]
|
||||||
assert validated.runtime_manifest_sha256 == payload["metadata"]["runtime_manifest_sha256"]
|
assert validated.runtime_manifest_sha256 == payload["metadata"]["runtime_manifest_sha256"]
|
||||||
assert module._write_manifest_atomically(manifest_path, payload) is False
|
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
|
||||||
|
|||||||
@@ -243,15 +243,6 @@ def migrate(args: argparse.Namespace) -> tuple[int, dict[str, Any]]:
|
|||||||
created_manifest = False
|
created_manifest = False
|
||||||
try:
|
try:
|
||||||
source = SourceRegistryService.ensure_server_owned_source(db, RuntimeModelProvenanceService.SOURCE_REGISTRY_KEY)
|
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(
|
snapshot = SourceRegistryService.record_snapshot(
|
||||||
db,
|
db,
|
||||||
|
|||||||
Reference in New Issue
Block a user