Initial public ModelForge release
This commit is contained in:
@@ -0,0 +1,222 @@
|
||||
"""M0 domain foundation.
|
||||
|
||||
Revision ID: 20260824_0001
|
||||
Revises: None
|
||||
"""
|
||||
|
||||
import sqlalchemy as sa
|
||||
|
||||
from alembic import op
|
||||
from modelforge_api.persistence.models import Base
|
||||
|
||||
revision = "20260824_0001"
|
||||
down_revision = None
|
||||
branch_labels = None
|
||||
depends_on = None
|
||||
|
||||
|
||||
def upgrade() -> None:
|
||||
# The declarative metadata is the reviewed M0 schema contract. create_all is
|
||||
# used only from this baseline migration; later changes require explicit ops.
|
||||
Base.metadata.create_all(bind=op.get_bind(), checkfirst=False)
|
||||
# Freeze the original M0 shape even though the declarative metadata evolves.
|
||||
# Remove newer foreign-key columns before dropping the tables they target.
|
||||
for column in (
|
||||
"profile_config",
|
||||
"health_contract",
|
||||
"device_policy",
|
||||
"modality",
|
||||
"dtype",
|
||||
"version",
|
||||
"artifact_set_id",
|
||||
"runtime_environment_id",
|
||||
):
|
||||
op.drop_column("runtime_profiles", column)
|
||||
for table in (
|
||||
# Later milestone tables are present in the evolving declarative metadata
|
||||
# used to build this baseline. Drop dependants before their M5/M0 targets
|
||||
# so a fresh upgrade still reconstructs each historical revision exactly.
|
||||
"audit_chain_heads",
|
||||
"node_decommission_operations",
|
||||
# M15 recovery tables come first: artifact_recovery_operations references
|
||||
# artifact_jobs, download_plans, artifact_sets, model_revisions and storage_roots,
|
||||
# all of which are dropped further down this list.
|
||||
"artifact_recovery_operations",
|
||||
"restore_operation_events",
|
||||
"restore_operations",
|
||||
"restore_plans",
|
||||
"backup_manifest_entries",
|
||||
"backup_sets",
|
||||
"recovery_asset_records",
|
||||
"recovery_policy_revisions",
|
||||
"incident_timeline_events",
|
||||
"alert_history_events",
|
||||
"operational_alerts",
|
||||
"operational_incidents",
|
||||
"maintenance_windows",
|
||||
"alert_rule_revisions",
|
||||
"slo_evaluations",
|
||||
"slo_policy_revisions",
|
||||
"service_level_indicators",
|
||||
"capacity_aggregates",
|
||||
"capacity_snapshots",
|
||||
"migration_events",
|
||||
"migration_cutover_operations",
|
||||
"migration_rollback_snapshots",
|
||||
"migration_shadow_sessions",
|
||||
"migration_validation_snapshots",
|
||||
"migration_batch_checkpoints",
|
||||
"migration_plans",
|
||||
"migration_validation_policy_revisions",
|
||||
"artifact_location_removal_records",
|
||||
"lifecycle_canary_runs",
|
||||
"lifecycle_operations",
|
||||
"lifecycle_rollback_snapshots",
|
||||
"lifecycle_promotion_plans",
|
||||
"lifecycle_approval_evidence",
|
||||
"lifecycle_approval_requests",
|
||||
"lifecycle_cleanup_plans",
|
||||
"lifecycle_retention_records",
|
||||
"retention_policy_revisions",
|
||||
"lifecycle_events",
|
||||
"lifecycle_policy_revisions",
|
||||
"lifecycle_subjects",
|
||||
"project_fit_evidence",
|
||||
"scheduler_evictions",
|
||||
"co_residency_evidence",
|
||||
"placement_plans",
|
||||
"scheduler_policy_revisions",
|
||||
"scheduler_accelerator_states",
|
||||
"capability_evaluation_runs",
|
||||
"capability_evaluation_suites",
|
||||
"reranking_case_results",
|
||||
"reranking_evaluation_runs",
|
||||
"retrieval_candidate_pools",
|
||||
"advisor_recommendations",
|
||||
"retrieval_pipeline_identities",
|
||||
"discovery_candidate_assessments",
|
||||
"advisor_policies",
|
||||
"model_comparisons",
|
||||
"evaluation_comparisons",
|
||||
"evaluation_case_results",
|
||||
"evaluation_runs",
|
||||
"evaluation_cases",
|
||||
"evaluation_suite_revisions",
|
||||
"evaluation_suites",
|
||||
"embedding_migrations",
|
||||
"project_evaluation_bindings",
|
||||
"serving_gpu_leases",
|
||||
"serving_jobs",
|
||||
"gateway_requests",
|
||||
"residency_allocations",
|
||||
"service_credentials",
|
||||
"service_clients",
|
||||
"capability_resource_envelopes",
|
||||
"capability_experiment_routes",
|
||||
"capability_deployments",
|
||||
"embedding_spaces",
|
||||
"production_execution_approvals",
|
||||
"deployment_candidates",
|
||||
"runtime_probe_metrics",
|
||||
"runtime_probes",
|
||||
"execution_approvals",
|
||||
"runtime_compatibility_assessments",
|
||||
"artifact_set_members",
|
||||
"artifact_inspections",
|
||||
"artifact_job_attempts",
|
||||
"artifact_jobs",
|
||||
"download_plan_files",
|
||||
"download_plans",
|
||||
"artifact_sets",
|
||||
"upstream_files",
|
||||
"upstream_snapshots",
|
||||
"artifact_locations",
|
||||
"derived_artifact_sources",
|
||||
"storage_roots",
|
||||
):
|
||||
op.drop_table(table)
|
||||
op.drop_table("runtime_environments")
|
||||
for column in ("source_type", "upstream_metadata", "local_metadata", "interpretation_metadata", "description"):
|
||||
op.drop_column("models", column)
|
||||
op.drop_column("model_revisions", "archived_at")
|
||||
op.drop_column("audit_events", "canonical_payload")
|
||||
op.drop_column("audit_events", "hash_format")
|
||||
for column in ("status", "verification_details", "archived_at"):
|
||||
op.drop_column("model_artifacts", column)
|
||||
for column in ("transformation_type", "environment_snapshot", "status", "verification_details"):
|
||||
op.drop_column("derived_artifacts", column)
|
||||
for table in (
|
||||
"node_credentials",
|
||||
"node_enrollments",
|
||||
"hardware_inventory_runs",
|
||||
"storage_volume_states",
|
||||
"accelerator_telemetry_latest",
|
||||
"host_telemetry_latest",
|
||||
):
|
||||
op.drop_table(table)
|
||||
for column in (
|
||||
"inventory_at",
|
||||
"last_seen_at",
|
||||
"first_seen_at",
|
||||
"inventory_source",
|
||||
"status_reason",
|
||||
"status",
|
||||
"mig_mode_current",
|
||||
"total_vram_bytes",
|
||||
"compute_capability_minor",
|
||||
"compute_capability_major",
|
||||
"architecture",
|
||||
"vendor",
|
||||
"pci_bus_id",
|
||||
):
|
||||
op.drop_column("accelerators", column)
|
||||
for column in (
|
||||
"decommissioned_by",
|
||||
"decommission_reason",
|
||||
"decommissioned_at",
|
||||
"generation",
|
||||
"telemetry_sequence",
|
||||
"inventory_sequence",
|
||||
"last_connection_error",
|
||||
"last_telemetry_observed_at",
|
||||
"last_telemetry_received_at",
|
||||
"last_inventory_observed_at",
|
||||
"last_inventory_received_at",
|
||||
"last_heartbeat_at",
|
||||
"agent_started_at",
|
||||
"agent_capabilities",
|
||||
"agent_protocol_version",
|
||||
"liveness_state",
|
||||
"observation_source",
|
||||
"benchmark_eligible",
|
||||
"lab_eligible",
|
||||
"production_eligible",
|
||||
"labels",
|
||||
"role",
|
||||
"enabled",
|
||||
):
|
||||
op.drop_column("compute_nodes", column)
|
||||
for column in (
|
||||
"inventory_at",
|
||||
"first_seen_at",
|
||||
"status_reason",
|
||||
"agent_version",
|
||||
"total_ram_bytes",
|
||||
"physical_core_count",
|
||||
"logical_cpu_count",
|
||||
"cpu_model",
|
||||
"kernel_version",
|
||||
"architecture",
|
||||
"os_version",
|
||||
"os_name",
|
||||
"identity_source",
|
||||
"display_name",
|
||||
):
|
||||
op.drop_column("compute_nodes", column)
|
||||
|
||||
|
||||
def downgrade() -> None:
|
||||
existing = set(sa.inspect(op.get_bind()).get_table_names())
|
||||
for table in reversed(Base.metadata.sorted_tables):
|
||||
if table.name in existing:
|
||||
op.drop_table(table.name)
|
||||
@@ -0,0 +1,194 @@
|
||||
"""M1 hardware plane persistence.
|
||||
|
||||
Revision ID: 20260824_0002
|
||||
Revises: 20260824_0001
|
||||
"""
|
||||
|
||||
import sqlalchemy as sa
|
||||
|
||||
from alembic import op
|
||||
|
||||
revision = "20260824_0002"
|
||||
down_revision = "20260824_0001"
|
||||
branch_labels = None
|
||||
depends_on = None
|
||||
|
||||
|
||||
def upgrade() -> None:
|
||||
for column in (
|
||||
sa.Column("display_name", sa.String(255), nullable=False, server_default=""),
|
||||
sa.Column("identity_source", sa.String(32), nullable=False, server_default="unknown"),
|
||||
sa.Column("os_name", sa.String(128)),
|
||||
sa.Column("os_version", sa.Text()),
|
||||
sa.Column("architecture", sa.String(128)),
|
||||
sa.Column("kernel_version", sa.String(255)),
|
||||
sa.Column("cpu_model", sa.String(255)),
|
||||
sa.Column("logical_cpu_count", sa.Integer()),
|
||||
sa.Column("physical_core_count", sa.Integer()),
|
||||
sa.Column("total_ram_bytes", sa.BigInteger()),
|
||||
sa.Column("agent_version", sa.String(64)),
|
||||
sa.Column("status_reason", sa.Text()),
|
||||
sa.Column(
|
||||
"first_seen_at",
|
||||
sa.DateTime(timezone=True),
|
||||
nullable=False,
|
||||
server_default=sa.func.now(),
|
||||
),
|
||||
sa.Column("inventory_at", sa.DateTime(timezone=True)),
|
||||
):
|
||||
op.add_column("compute_nodes", column)
|
||||
for column in (
|
||||
sa.Column("pci_bus_id", sa.String(64)),
|
||||
sa.Column("vendor", sa.String(64), nullable=False, server_default="NVIDIA"),
|
||||
sa.Column("architecture", sa.String(64)),
|
||||
sa.Column("compute_capability_major", sa.Integer()),
|
||||
sa.Column("compute_capability_minor", sa.Integer()),
|
||||
sa.Column("total_vram_bytes", sa.BigInteger()),
|
||||
sa.Column("mig_mode_current", sa.Boolean()),
|
||||
sa.Column("status", sa.String(32), nullable=False, server_default="active"),
|
||||
sa.Column("status_reason", sa.Text()),
|
||||
sa.Column("inventory_source", sa.String(32), nullable=False, server_default="nvidia_nvml"),
|
||||
sa.Column(
|
||||
"first_seen_at",
|
||||
sa.DateTime(timezone=True),
|
||||
nullable=False,
|
||||
server_default=sa.func.now(),
|
||||
),
|
||||
sa.Column("last_seen_at", sa.DateTime(timezone=True)),
|
||||
sa.Column("inventory_at", sa.DateTime(timezone=True)),
|
||||
):
|
||||
op.add_column("accelerators", column)
|
||||
op.create_table(
|
||||
"host_telemetry_latest",
|
||||
sa.Column(
|
||||
"compute_node_id",
|
||||
sa.Uuid(),
|
||||
sa.ForeignKey("compute_nodes.id", ondelete="CASCADE"),
|
||||
nullable=False,
|
||||
unique=True,
|
||||
),
|
||||
sa.Column("available_ram_bytes", sa.BigInteger()),
|
||||
sa.Column("availability", sa.JSON(), nullable=False),
|
||||
sa.Column("observed_at", sa.DateTime(timezone=True), nullable=False),
|
||||
sa.Column("id", sa.Uuid(), primary_key=True),
|
||||
)
|
||||
op.create_index(
|
||||
"ix_host_telemetry_latest_compute_node_id",
|
||||
"host_telemetry_latest",
|
||||
["compute_node_id"],
|
||||
unique=True,
|
||||
)
|
||||
op.create_table(
|
||||
"accelerator_telemetry_latest",
|
||||
sa.Column(
|
||||
"accelerator_id",
|
||||
sa.Uuid(),
|
||||
sa.ForeignKey("accelerators.id", ondelete="CASCADE"),
|
||||
nullable=False,
|
||||
unique=True,
|
||||
),
|
||||
sa.Column("used_vram_bytes", sa.BigInteger()),
|
||||
sa.Column("free_vram_bytes", sa.BigInteger()),
|
||||
sa.Column("gpu_utilization_percent", sa.Integer()),
|
||||
sa.Column("memory_utilization_percent", sa.Integer()),
|
||||
sa.Column("temperature_c", sa.Integer()),
|
||||
sa.Column("power_draw_w", sa.Float()),
|
||||
sa.Column("power_limit_w", sa.Float()),
|
||||
sa.Column("graphics_clock_mhz", sa.Integer()),
|
||||
sa.Column("memory_clock_mhz", sa.Integer()),
|
||||
sa.Column("fan_speed_percent", sa.Integer()),
|
||||
sa.Column("performance_state", sa.String(32)),
|
||||
sa.Column("availability", sa.JSON(), nullable=False),
|
||||
sa.Column("observed_at", sa.DateTime(timezone=True), nullable=False),
|
||||
sa.Column("id", sa.Uuid(), primary_key=True),
|
||||
)
|
||||
op.create_index(
|
||||
"ix_accelerator_telemetry_latest_accelerator_id",
|
||||
"accelerator_telemetry_latest",
|
||||
["accelerator_id"],
|
||||
unique=True,
|
||||
)
|
||||
op.create_table(
|
||||
"storage_volume_states",
|
||||
sa.Column(
|
||||
"compute_node_id",
|
||||
sa.Uuid(),
|
||||
sa.ForeignKey("compute_nodes.id", ondelete="CASCADE"),
|
||||
nullable=False,
|
||||
),
|
||||
sa.Column("purpose", sa.String(64), nullable=False),
|
||||
sa.Column("path", sa.Text(), nullable=False),
|
||||
sa.Column("total_bytes", sa.BigInteger()),
|
||||
sa.Column("used_bytes", sa.BigInteger()),
|
||||
sa.Column("free_bytes", sa.BigInteger()),
|
||||
sa.Column("availability", sa.JSON(), nullable=False),
|
||||
sa.Column("observed_at", sa.DateTime(timezone=True), nullable=False),
|
||||
sa.Column("id", sa.Uuid(), primary_key=True),
|
||||
sa.UniqueConstraint("compute_node_id", "purpose", "path", name="uq_node_storage_path"),
|
||||
)
|
||||
op.create_index(
|
||||
"ix_storage_volume_states_compute_node_id", "storage_volume_states", ["compute_node_id"]
|
||||
)
|
||||
op.create_table(
|
||||
"hardware_inventory_runs",
|
||||
sa.Column(
|
||||
"compute_node_id", sa.Uuid(), sa.ForeignKey("compute_nodes.id", ondelete="SET NULL")
|
||||
),
|
||||
sa.Column("status", sa.String(32), nullable=False),
|
||||
sa.Column("source", sa.String(64), nullable=False),
|
||||
sa.Column("fingerprint", sa.String(64)),
|
||||
sa.Column("summary", sa.JSON(), nullable=False),
|
||||
sa.Column("error", sa.Text()),
|
||||
sa.Column("started_at", sa.DateTime(timezone=True), nullable=False),
|
||||
sa.Column("completed_at", sa.DateTime(timezone=True)),
|
||||
sa.Column("id", sa.Uuid(), primary_key=True),
|
||||
)
|
||||
op.create_index(
|
||||
"ix_hardware_inventory_runs_compute_node_id", "hardware_inventory_runs", ["compute_node_id"]
|
||||
)
|
||||
op.create_index(
|
||||
"ix_hardware_inventory_runs_fingerprint", "hardware_inventory_runs", ["fingerprint"]
|
||||
)
|
||||
|
||||
|
||||
def downgrade() -> None:
|
||||
for table in (
|
||||
"hardware_inventory_runs",
|
||||
"storage_volume_states",
|
||||
"accelerator_telemetry_latest",
|
||||
"host_telemetry_latest",
|
||||
):
|
||||
op.drop_table(table)
|
||||
for column in (
|
||||
"inventory_at",
|
||||
"last_seen_at",
|
||||
"first_seen_at",
|
||||
"inventory_source",
|
||||
"status_reason",
|
||||
"status",
|
||||
"mig_mode_current",
|
||||
"total_vram_bytes",
|
||||
"compute_capability_minor",
|
||||
"compute_capability_major",
|
||||
"architecture",
|
||||
"vendor",
|
||||
"pci_bus_id",
|
||||
):
|
||||
op.drop_column("accelerators", column)
|
||||
for column in (
|
||||
"inventory_at",
|
||||
"first_seen_at",
|
||||
"status_reason",
|
||||
"agent_version",
|
||||
"total_ram_bytes",
|
||||
"physical_core_count",
|
||||
"logical_cpu_count",
|
||||
"cpu_model",
|
||||
"kernel_version",
|
||||
"architecture",
|
||||
"os_version",
|
||||
"os_name",
|
||||
"identity_source",
|
||||
"display_name",
|
||||
):
|
||||
op.drop_column("compute_nodes", column)
|
||||
@@ -0,0 +1,141 @@
|
||||
"""M1.5 remote compute node agent.
|
||||
|
||||
Revision ID: 20260825_0003
|
||||
Revises: 20260824_0002
|
||||
"""
|
||||
|
||||
import sqlalchemy as sa
|
||||
|
||||
from alembic import op
|
||||
|
||||
revision = "20260825_0003"
|
||||
down_revision = "20260824_0002"
|
||||
branch_labels = None
|
||||
depends_on = None
|
||||
|
||||
|
||||
def upgrade() -> None:
|
||||
for column in (
|
||||
sa.Column("enabled", sa.Boolean(), nullable=False, server_default=sa.true()),
|
||||
sa.Column("role", sa.String(64)),
|
||||
sa.Column("labels", sa.JSON(), nullable=False, server_default=sa.text("'{}'")),
|
||||
sa.Column("production_eligible", sa.Boolean(), nullable=False, server_default=sa.false()),
|
||||
sa.Column("lab_eligible", sa.Boolean(), nullable=False, server_default=sa.true()),
|
||||
sa.Column("benchmark_eligible", sa.Boolean(), nullable=False, server_default=sa.false()),
|
||||
sa.Column(
|
||||
"observation_source",
|
||||
sa.String(32),
|
||||
nullable=False,
|
||||
server_default="local_control_plane",
|
||||
),
|
||||
sa.Column("liveness_state", sa.String(32), nullable=False, server_default="offline"),
|
||||
sa.Column("agent_protocol_version", sa.Integer()),
|
||||
sa.Column("agent_capabilities", sa.JSON(), nullable=False, server_default=sa.text("'[]'")),
|
||||
sa.Column("agent_started_at", sa.DateTime(timezone=True)),
|
||||
sa.Column("last_heartbeat_at", sa.DateTime(timezone=True)),
|
||||
sa.Column("last_inventory_received_at", sa.DateTime(timezone=True)),
|
||||
sa.Column("last_inventory_observed_at", sa.DateTime(timezone=True)),
|
||||
sa.Column("last_telemetry_received_at", sa.DateTime(timezone=True)),
|
||||
sa.Column("last_telemetry_observed_at", sa.DateTime(timezone=True)),
|
||||
sa.Column("last_connection_error", sa.Text()),
|
||||
sa.Column("inventory_sequence", sa.BigInteger(), nullable=False, server_default="0"),
|
||||
sa.Column("telemetry_sequence", sa.BigInteger(), nullable=False, server_default="0"),
|
||||
):
|
||||
op.add_column("compute_nodes", column)
|
||||
|
||||
for table in (
|
||||
"host_telemetry_latest",
|
||||
"accelerator_telemetry_latest",
|
||||
"storage_volume_states",
|
||||
):
|
||||
op.add_column(table, sa.Column("received_at", sa.DateTime(timezone=True)))
|
||||
for column in (
|
||||
sa.Column("observation_sequence", sa.BigInteger()),
|
||||
sa.Column("observed_at", sa.DateTime(timezone=True)),
|
||||
sa.Column("received_at", sa.DateTime(timezone=True)),
|
||||
):
|
||||
op.add_column("hardware_inventory_runs", column)
|
||||
|
||||
op.create_table(
|
||||
"node_enrollments",
|
||||
sa.Column("token_hash", sa.String(64), nullable=False, unique=True),
|
||||
sa.Column("scope", sa.String(64), nullable=False),
|
||||
sa.Column("requested_metadata", sa.JSON(), nullable=False),
|
||||
sa.Column(
|
||||
"created_at", sa.DateTime(timezone=True), nullable=False, server_default=sa.func.now()
|
||||
),
|
||||
sa.Column("expires_at", sa.DateTime(timezone=True), nullable=False),
|
||||
sa.Column("used_at", sa.DateTime(timezone=True)),
|
||||
sa.Column("revoked_at", sa.DateTime(timezone=True)),
|
||||
sa.Column(
|
||||
"enrolled_node_id",
|
||||
sa.Uuid(),
|
||||
sa.ForeignKey("compute_nodes.id", ondelete="SET NULL"),
|
||||
),
|
||||
sa.Column("id", sa.Uuid(), primary_key=True),
|
||||
)
|
||||
op.create_index("ix_node_enrollments_expires_at", "node_enrollments", ["expires_at"])
|
||||
op.create_index(
|
||||
"ix_node_enrollments_enrolled_node_id", "node_enrollments", ["enrolled_node_id"]
|
||||
)
|
||||
op.create_table(
|
||||
"node_credentials",
|
||||
sa.Column(
|
||||
"compute_node_id",
|
||||
sa.Uuid(),
|
||||
sa.ForeignKey("compute_nodes.id", ondelete="CASCADE"),
|
||||
nullable=False,
|
||||
),
|
||||
sa.Column("secret_hash", sa.String(64), nullable=False, unique=True),
|
||||
sa.Column("scope", sa.String(64), nullable=False),
|
||||
sa.Column(
|
||||
"created_at", sa.DateTime(timezone=True), nullable=False, server_default=sa.func.now()
|
||||
),
|
||||
sa.Column("last_used_at", sa.DateTime(timezone=True)),
|
||||
sa.Column("revoked_at", sa.DateTime(timezone=True)),
|
||||
sa.Column("id", sa.Uuid(), primary_key=True),
|
||||
)
|
||||
op.create_index("ix_node_credentials_compute_node_id", "node_credentials", ["compute_node_id"])
|
||||
op.create_index(
|
||||
"uq_active_node_credential",
|
||||
"node_credentials",
|
||||
["compute_node_id"],
|
||||
unique=True,
|
||||
postgresql_where=sa.text("revoked_at IS NULL"),
|
||||
sqlite_where=sa.text("revoked_at IS NULL"),
|
||||
)
|
||||
|
||||
|
||||
def downgrade() -> None:
|
||||
op.drop_table("node_credentials")
|
||||
op.drop_table("node_enrollments")
|
||||
for column in ("received_at", "observed_at", "observation_sequence"):
|
||||
op.drop_column("hardware_inventory_runs", column)
|
||||
for table in (
|
||||
"storage_volume_states",
|
||||
"accelerator_telemetry_latest",
|
||||
"host_telemetry_latest",
|
||||
):
|
||||
op.drop_column(table, "received_at")
|
||||
for column in (
|
||||
"telemetry_sequence",
|
||||
"inventory_sequence",
|
||||
"last_connection_error",
|
||||
"last_telemetry_observed_at",
|
||||
"last_telemetry_received_at",
|
||||
"last_inventory_observed_at",
|
||||
"last_inventory_received_at",
|
||||
"last_heartbeat_at",
|
||||
"agent_started_at",
|
||||
"agent_capabilities",
|
||||
"agent_protocol_version",
|
||||
"liveness_state",
|
||||
"observation_source",
|
||||
"benchmark_eligible",
|
||||
"lab_eligible",
|
||||
"production_eligible",
|
||||
"labels",
|
||||
"role",
|
||||
"enabled",
|
||||
):
|
||||
op.drop_column("compute_nodes", column)
|
||||
@@ -0,0 +1,170 @@
|
||||
"""M2 operational model registry and artifact provenance.
|
||||
|
||||
Revision ID: 20260825_0004
|
||||
Revises: 20260825_0003
|
||||
"""
|
||||
|
||||
import sqlalchemy as sa
|
||||
|
||||
from alembic import op
|
||||
|
||||
revision = "20260825_0004"
|
||||
down_revision = "20260825_0003"
|
||||
branch_labels = None
|
||||
depends_on = None
|
||||
|
||||
|
||||
def upgrade() -> None:
|
||||
for column in (
|
||||
sa.Column("source_type", sa.String(32), nullable=False, server_default="huggingface"),
|
||||
sa.Column("upstream_metadata", sa.JSON(), nullable=False, server_default=sa.text("'{}'")),
|
||||
sa.Column("local_metadata", sa.JSON(), nullable=False, server_default=sa.text("'{}'")),
|
||||
sa.Column(
|
||||
"interpretation_metadata", sa.JSON(), nullable=False, server_default=sa.text("'{}'")
|
||||
),
|
||||
sa.Column("description", sa.Text()),
|
||||
):
|
||||
op.add_column("models", column)
|
||||
op.add_column("model_revisions", sa.Column("archived_at", sa.DateTime(timezone=True)))
|
||||
for column in (
|
||||
sa.Column("status", sa.String(32), nullable=False, server_default="remote"),
|
||||
sa.Column(
|
||||
"verification_details", sa.JSON(), nullable=False, server_default=sa.text("'{}'")
|
||||
),
|
||||
sa.Column("archived_at", sa.DateTime(timezone=True)),
|
||||
):
|
||||
op.add_column("model_artifacts", column)
|
||||
op.alter_column("model_artifacts", "storage_uri", existing_type=sa.Text(), nullable=True)
|
||||
for column in (
|
||||
sa.Column(
|
||||
"transformation_type", sa.String(64), nullable=False, server_default="conversion"
|
||||
),
|
||||
sa.Column(
|
||||
"environment_snapshot", sa.JSON(), nullable=False, server_default=sa.text("'{}'")
|
||||
),
|
||||
sa.Column("status", sa.String(32), nullable=False, server_default="remote"),
|
||||
sa.Column(
|
||||
"verification_details", sa.JSON(), nullable=False, server_default=sa.text("'{}'")
|
||||
),
|
||||
):
|
||||
op.add_column("derived_artifacts", column)
|
||||
op.alter_column("derived_artifacts", "storage_uri", existing_type=sa.Text(), nullable=True)
|
||||
op.alter_column(
|
||||
"derived_artifacts", "source_artifact_id", existing_type=sa.Uuid(), nullable=True
|
||||
)
|
||||
|
||||
op.create_table(
|
||||
"storage_roots",
|
||||
sa.Column("compute_node_id", sa.Uuid(), nullable=False),
|
||||
sa.Column("name", sa.String(128), nullable=False),
|
||||
sa.Column("purpose", sa.String(64), nullable=False, server_default="model_artifacts"),
|
||||
sa.Column("path", sa.Text(), nullable=False),
|
||||
sa.Column("status", sa.String(32), nullable=False, server_default="unknown"),
|
||||
sa.Column("writable", sa.Boolean(), nullable=False, server_default=sa.false()),
|
||||
sa.Column("capacity_bytes", sa.BigInteger()),
|
||||
sa.Column("free_bytes", sa.BigInteger()),
|
||||
sa.Column("reserve_bytes", sa.BigInteger(), nullable=False, server_default="0"),
|
||||
sa.Column("reserve_percent", sa.Integer(), nullable=False, server_default="10"),
|
||||
sa.Column("capacity_observed_at", sa.DateTime(timezone=True)),
|
||||
sa.Column("validation_details", sa.JSON(), nullable=False, server_default=sa.text("'{}'")),
|
||||
sa.Column("deprecated_at", sa.DateTime(timezone=True)),
|
||||
sa.Column("id", sa.Uuid(), primary_key=True),
|
||||
sa.Column("created_at", sa.DateTime(timezone=True), server_default=sa.func.now()),
|
||||
sa.Column("updated_at", sa.DateTime(timezone=True), server_default=sa.func.now()),
|
||||
sa.ForeignKeyConstraint(["compute_node_id"], ["compute_nodes.id"], ondelete="RESTRICT"),
|
||||
sa.UniqueConstraint("compute_node_id", "path", name="uq_storage_root_node_path"),
|
||||
sa.CheckConstraint("reserve_bytes >= 0", name="ck_storage_root_reserve_bytes"),
|
||||
sa.CheckConstraint(
|
||||
"reserve_percent >= 0 AND reserve_percent <= 100",
|
||||
name="ck_storage_root_reserve_percent",
|
||||
),
|
||||
)
|
||||
op.create_index("ix_storage_roots_compute_node_id", "storage_roots", ["compute_node_id"])
|
||||
op.create_table(
|
||||
"derived_artifact_sources",
|
||||
sa.Column("derived_artifact_id", sa.Uuid(), nullable=False),
|
||||
sa.Column("source_artifact_id", sa.Uuid(), nullable=False),
|
||||
sa.Column("ordinal", sa.Integer(), nullable=False, server_default="0"),
|
||||
sa.Column("source_sha256", sa.String(64), nullable=False),
|
||||
sa.ForeignKeyConstraint(
|
||||
["derived_artifact_id"], ["derived_artifacts.id"], ondelete="RESTRICT"
|
||||
),
|
||||
sa.ForeignKeyConstraint(
|
||||
["source_artifact_id"], ["model_artifacts.id"], ondelete="RESTRICT"
|
||||
),
|
||||
sa.PrimaryKeyConstraint("derived_artifact_id", "source_artifact_id"),
|
||||
sa.UniqueConstraint(
|
||||
"derived_artifact_id", "source_artifact_id", name="uq_derived_artifact_source"
|
||||
),
|
||||
)
|
||||
op.create_table(
|
||||
"artifact_locations",
|
||||
sa.Column("artifact_id", sa.Uuid()),
|
||||
sa.Column("derived_artifact_id", sa.Uuid()),
|
||||
sa.Column("storage_root_id", sa.Uuid(), nullable=False),
|
||||
sa.Column("relative_path", sa.Text(), nullable=False),
|
||||
sa.Column("status", sa.String(32), nullable=False, server_default="unknown"),
|
||||
sa.Column("size_bytes", sa.BigInteger()),
|
||||
sa.Column("observed_sha256", sa.String(64)),
|
||||
sa.Column("last_checked_at", sa.DateTime(timezone=True)),
|
||||
sa.Column("id", sa.Uuid(), primary_key=True),
|
||||
sa.Column("created_at", sa.DateTime(timezone=True), server_default=sa.func.now()),
|
||||
sa.Column("updated_at", sa.DateTime(timezone=True), server_default=sa.func.now()),
|
||||
sa.ForeignKeyConstraint(["artifact_id"], ["model_artifacts.id"], ondelete="RESTRICT"),
|
||||
sa.ForeignKeyConstraint(
|
||||
["derived_artifact_id"], ["derived_artifacts.id"], ondelete="RESTRICT"
|
||||
),
|
||||
sa.ForeignKeyConstraint(["storage_root_id"], ["storage_roots.id"], ondelete="RESTRICT"),
|
||||
sa.CheckConstraint(
|
||||
"(artifact_id IS NOT NULL AND derived_artifact_id IS NULL) OR "
|
||||
"(artifact_id IS NULL AND derived_artifact_id IS NOT NULL)",
|
||||
name="ck_artifact_location_one_owner",
|
||||
),
|
||||
sa.UniqueConstraint(
|
||||
"storage_root_id", "relative_path", name="uq_storage_root_relative_path"
|
||||
),
|
||||
)
|
||||
op.create_index("ix_artifact_locations_artifact_id", "artifact_locations", ["artifact_id"])
|
||||
op.create_index(
|
||||
"ix_artifact_locations_derived_artifact_id",
|
||||
"artifact_locations",
|
||||
["derived_artifact_id"],
|
||||
)
|
||||
op.create_index(
|
||||
"ix_artifact_locations_storage_root_id", "artifact_locations", ["storage_root_id"]
|
||||
)
|
||||
|
||||
|
||||
def downgrade() -> None:
|
||||
op.drop_table("artifact_locations")
|
||||
op.drop_table("derived_artifact_sources")
|
||||
op.drop_table("storage_roots")
|
||||
op.execute(
|
||||
"UPDATE model_artifacts SET storage_uri = 'registry://artifact/' || id::text "
|
||||
"WHERE storage_uri IS NULL"
|
||||
)
|
||||
op.execute(
|
||||
"UPDATE derived_artifacts SET storage_uri = 'registry://derived/' || id::text "
|
||||
"WHERE storage_uri IS NULL"
|
||||
)
|
||||
op.alter_column("derived_artifacts", "source_artifact_id", existing_type=sa.Uuid(), nullable=False)
|
||||
op.alter_column("derived_artifacts", "storage_uri", existing_type=sa.Text(), nullable=False)
|
||||
for column in (
|
||||
"verification_details",
|
||||
"status",
|
||||
"environment_snapshot",
|
||||
"transformation_type",
|
||||
):
|
||||
op.drop_column("derived_artifacts", column)
|
||||
op.alter_column("model_artifacts", "storage_uri", existing_type=sa.Text(), nullable=False)
|
||||
for column in ("archived_at", "verification_details", "status"):
|
||||
op.drop_column("model_artifacts", column)
|
||||
op.drop_column("model_revisions", "archived_at")
|
||||
for column in (
|
||||
"description",
|
||||
"interpretation_metadata",
|
||||
"local_metadata",
|
||||
"upstream_metadata",
|
||||
"source_type",
|
||||
):
|
||||
op.drop_column("models", column)
|
||||
@@ -0,0 +1,210 @@
|
||||
"""M3 Hugging Face discovery and node-local artifact acquisition.
|
||||
|
||||
Revision ID: 20260825_0005
|
||||
Revises: 20260825_0004
|
||||
"""
|
||||
|
||||
import sqlalchemy as sa
|
||||
|
||||
from alembic import op
|
||||
|
||||
revision = "20260825_0005"
|
||||
down_revision = "20260825_0004"
|
||||
branch_labels = None
|
||||
depends_on = None
|
||||
|
||||
|
||||
def _identity() -> list[sa.Column]:
|
||||
return [
|
||||
sa.Column("id", sa.Uuid(), primary_key=True),
|
||||
sa.Column("created_at", sa.DateTime(timezone=True), server_default=sa.func.now()),
|
||||
sa.Column("updated_at", sa.DateTime(timezone=True), server_default=sa.func.now()),
|
||||
]
|
||||
|
||||
|
||||
def upgrade() -> None:
|
||||
op.add_column("storage_roots", sa.Column("agent_path", sa.Text()))
|
||||
op.create_table(
|
||||
"upstream_snapshots",
|
||||
sa.Column("model_id", sa.Uuid()),
|
||||
sa.Column("provider", sa.String(64), nullable=False, server_default="huggingface"),
|
||||
sa.Column("repository_id", sa.String(255), nullable=False),
|
||||
sa.Column("requested_revision", sa.String(255), nullable=False, server_default="main"),
|
||||
sa.Column("resolved_commit_sha", sa.String(64), nullable=False),
|
||||
sa.Column("access_state", sa.String(32), nullable=False, server_default="public"),
|
||||
sa.Column("metadata_snapshot", sa.JSON(), nullable=False, server_default=sa.text("'{}'")),
|
||||
sa.Column("card_metadata", sa.JSON(), nullable=False, server_default=sa.text("'{}'")),
|
||||
sa.Column("security_metadata", sa.JSON(), nullable=False, server_default=sa.text("'{}'")),
|
||||
sa.Column("source_updated_at", sa.DateTime(timezone=True)),
|
||||
sa.Column("observed_at", sa.DateTime(timezone=True), nullable=False, server_default=sa.func.now()),
|
||||
sa.Column("stale_after", sa.DateTime(timezone=True), nullable=False),
|
||||
sa.Column("id", sa.Uuid(), primary_key=True),
|
||||
sa.ForeignKeyConstraint(["model_id"], ["models.id"], ondelete="RESTRICT"),
|
||||
sa.CheckConstraint("length(resolved_commit_sha) >= 40", name="ck_snapshot_commit_length"),
|
||||
)
|
||||
op.create_index("ix_upstream_snapshots_model_id", "upstream_snapshots", ["model_id"])
|
||||
op.create_index("ix_upstream_snapshots_repository_id", "upstream_snapshots", ["repository_id"])
|
||||
op.create_index("ix_upstream_snapshots_resolved_commit_sha", "upstream_snapshots", ["resolved_commit_sha"])
|
||||
op.create_table(
|
||||
"upstream_files",
|
||||
sa.Column("snapshot_id", sa.Uuid(), nullable=False),
|
||||
sa.Column("path", sa.Text(), nullable=False),
|
||||
sa.Column("size_bytes", sa.BigInteger()),
|
||||
sa.Column("blob_id", sa.String(128)),
|
||||
sa.Column("upstream_sha256", sa.String(64)),
|
||||
sa.Column("file_format", sa.String(64), nullable=False, server_default="unknown"),
|
||||
sa.Column("role", sa.String(64), nullable=False, server_default="other"),
|
||||
sa.Column("risk_flags", sa.JSON(), nullable=False, server_default=sa.text("'[]'")),
|
||||
sa.Column("metadata_snapshot", sa.JSON(), nullable=False, server_default=sa.text("'{}'")),
|
||||
sa.Column("id", sa.Uuid(), primary_key=True),
|
||||
sa.ForeignKeyConstraint(["snapshot_id"], ["upstream_snapshots.id"], ondelete="RESTRICT"),
|
||||
sa.UniqueConstraint("snapshot_id", "path", name="uq_upstream_file_snapshot_path"),
|
||||
sa.CheckConstraint("size_bytes IS NULL OR size_bytes >= 0", name="ck_upstream_file_size"),
|
||||
)
|
||||
op.create_index("ix_upstream_files_snapshot_id", "upstream_files", ["snapshot_id"])
|
||||
op.create_table(
|
||||
"artifact_sets",
|
||||
sa.Column("revision_id", sa.Uuid(), nullable=False),
|
||||
sa.Column("snapshot_id", sa.Uuid(), nullable=False),
|
||||
sa.Column("variant_key", sa.String(128), nullable=False),
|
||||
sa.Column("label", sa.String(255), nullable=False),
|
||||
sa.Column("selection_reason", sa.Text(), nullable=False),
|
||||
sa.Column("selected_paths", sa.JSON(), nullable=False, server_default=sa.text("'[]'")),
|
||||
sa.Column("total_size_bytes", sa.BigInteger(), nullable=False, server_default="0"),
|
||||
sa.Column("file_count", sa.Integer(), nullable=False, server_default="0"),
|
||||
sa.Column("availability", sa.String(32), nullable=False, server_default="remote"),
|
||||
sa.Column("status", sa.String(32), nullable=False, server_default="remote"),
|
||||
sa.Column("completeness", sa.String(32), nullable=False, server_default="planned"),
|
||||
sa.Column("security_status", sa.String(32), nullable=False, server_default="unverified"),
|
||||
sa.Column("license_status", sa.String(32), nullable=False, server_default="unknown"),
|
||||
sa.Column("immutable_at", sa.DateTime(timezone=True), nullable=False),
|
||||
*_identity(),
|
||||
sa.ForeignKeyConstraint(["revision_id"], ["model_revisions.id"], ondelete="RESTRICT"),
|
||||
sa.ForeignKeyConstraint(["snapshot_id"], ["upstream_snapshots.id"], ondelete="RESTRICT"),
|
||||
sa.UniqueConstraint("revision_id", "variant_key", name="uq_artifact_set_revision_variant"),
|
||||
sa.CheckConstraint("total_size_bytes >= 0", name="ck_artifact_set_size"),
|
||||
)
|
||||
op.create_index("ix_artifact_sets_revision_id", "artifact_sets", ["revision_id"])
|
||||
op.create_index("ix_artifact_sets_snapshot_id", "artifact_sets", ["snapshot_id"])
|
||||
op.create_table(
|
||||
"download_plans",
|
||||
sa.Column("artifact_set_id", sa.Uuid(), nullable=False),
|
||||
sa.Column("compute_node_id", sa.Uuid(), nullable=False),
|
||||
sa.Column("storage_root_id", sa.Uuid(), nullable=False),
|
||||
sa.Column("repository_id", sa.String(255), nullable=False),
|
||||
sa.Column("resolved_commit_sha", sa.String(64), nullable=False),
|
||||
sa.Column("total_size_bytes", sa.BigInteger(), nullable=False),
|
||||
sa.Column("file_count", sa.Integer(), nullable=False),
|
||||
sa.Column("status", sa.String(32), nullable=False, server_default="ready"),
|
||||
sa.Column("idempotency_key", sa.String(64), nullable=False),
|
||||
sa.Column("preflight", sa.JSON(), nullable=False, server_default=sa.text("'{}'")),
|
||||
sa.Column("immutable_payload", sa.JSON(), nullable=False),
|
||||
sa.Column("planned_at", sa.DateTime(timezone=True), nullable=False, server_default=sa.func.now()),
|
||||
sa.Column("expires_at", sa.DateTime(timezone=True), nullable=False),
|
||||
sa.Column("immutable_at", sa.DateTime(timezone=True), nullable=False),
|
||||
*_identity(),
|
||||
sa.ForeignKeyConstraint(["artifact_set_id"], ["artifact_sets.id"], ondelete="RESTRICT"),
|
||||
sa.ForeignKeyConstraint(["compute_node_id"], ["compute_nodes.id"], ondelete="RESTRICT"),
|
||||
sa.ForeignKeyConstraint(["storage_root_id"], ["storage_roots.id"], ondelete="RESTRICT"),
|
||||
sa.UniqueConstraint("idempotency_key", name="uq_download_plan_idempotency"),
|
||||
sa.CheckConstraint("total_size_bytes >= 0", name="ck_download_plan_size"),
|
||||
)
|
||||
for col in ("artifact_set_id", "compute_node_id", "storage_root_id"):
|
||||
op.create_index(f"ix_download_plans_{col}", "download_plans", [col])
|
||||
op.create_table(
|
||||
"download_plan_files",
|
||||
sa.Column("plan_id", sa.Uuid(), nullable=False),
|
||||
sa.Column("ordinal", sa.Integer(), nullable=False),
|
||||
sa.Column("path", sa.Text(), nullable=False),
|
||||
sa.Column("size_bytes", sa.BigInteger(), nullable=False),
|
||||
sa.Column("upstream_sha256", sa.String(64)),
|
||||
sa.Column("file_format", sa.String(64), nullable=False),
|
||||
sa.Column("role", sa.String(64), nullable=False),
|
||||
sa.Column("risk_flags", sa.JSON(), nullable=False, server_default=sa.text("'[]'")),
|
||||
sa.Column("id", sa.Uuid(), primary_key=True),
|
||||
sa.ForeignKeyConstraint(["plan_id"], ["download_plans.id"], ondelete="RESTRICT"),
|
||||
sa.UniqueConstraint("plan_id", "path", name="uq_download_plan_file_path"),
|
||||
sa.UniqueConstraint("plan_id", "ordinal", name="uq_download_plan_file_ordinal"),
|
||||
)
|
||||
op.create_index("ix_download_plan_files_plan_id", "download_plan_files", ["plan_id"])
|
||||
op.create_table(
|
||||
"artifact_jobs",
|
||||
sa.Column("plan_id", sa.Uuid(), nullable=False),
|
||||
sa.Column("compute_node_id", sa.Uuid(), nullable=False),
|
||||
sa.Column("storage_root_id", sa.Uuid(), nullable=False),
|
||||
sa.Column("status", sa.String(32), nullable=False, server_default="queued"),
|
||||
sa.Column("idempotency_key", sa.String(64), nullable=False),
|
||||
sa.Column("attempt_count", sa.Integer(), nullable=False, server_default="0"),
|
||||
sa.Column("lease_token_hash", sa.String(64)),
|
||||
sa.Column("lease_expires_at", sa.DateTime(timezone=True)),
|
||||
sa.Column("progress_bytes", sa.BigInteger(), nullable=False, server_default="0"),
|
||||
sa.Column("total_bytes", sa.BigInteger(), nullable=False),
|
||||
sa.Column("current_file", sa.Text()),
|
||||
sa.Column("cancel_requested", sa.Boolean(), nullable=False, server_default=sa.false()),
|
||||
sa.Column("quarantine_relative_path", sa.Text()),
|
||||
sa.Column("promoted_relative_path", sa.Text()),
|
||||
sa.Column("error_code", sa.String(64)),
|
||||
sa.Column("error_message", sa.Text()),
|
||||
sa.Column("result", sa.JSON(), nullable=False, server_default=sa.text("'{}'")),
|
||||
sa.Column("started_at", sa.DateTime(timezone=True)),
|
||||
sa.Column("completed_at", sa.DateTime(timezone=True)),
|
||||
*_identity(),
|
||||
sa.ForeignKeyConstraint(["plan_id"], ["download_plans.id"], ondelete="RESTRICT"),
|
||||
sa.ForeignKeyConstraint(["compute_node_id"], ["compute_nodes.id"], ondelete="RESTRICT"),
|
||||
sa.ForeignKeyConstraint(["storage_root_id"], ["storage_roots.id"], ondelete="RESTRICT"),
|
||||
sa.UniqueConstraint("plan_id", name="uq_artifact_job_plan"),
|
||||
sa.UniqueConstraint("idempotency_key", name="uq_artifact_job_idempotency"),
|
||||
)
|
||||
for col in ("plan_id", "compute_node_id", "storage_root_id", "status"):
|
||||
op.create_index(f"ix_artifact_jobs_{col}", "artifact_jobs", [col])
|
||||
op.create_table(
|
||||
"artifact_job_attempts",
|
||||
sa.Column("job_id", sa.Uuid(), nullable=False),
|
||||
sa.Column("attempt", sa.Integer(), nullable=False),
|
||||
sa.Column("status", sa.String(32), nullable=False),
|
||||
sa.Column("started_at", sa.DateTime(timezone=True), nullable=False, server_default=sa.func.now()),
|
||||
sa.Column("completed_at", sa.DateTime(timezone=True)),
|
||||
sa.Column("details", sa.JSON(), nullable=False, server_default=sa.text("'{}'")),
|
||||
sa.Column("id", sa.Uuid(), primary_key=True),
|
||||
sa.ForeignKeyConstraint(["job_id"], ["artifact_jobs.id"], ondelete="RESTRICT"),
|
||||
)
|
||||
op.create_index("ix_artifact_job_attempts_job_id", "artifact_job_attempts", ["job_id"])
|
||||
op.create_table(
|
||||
"artifact_inspections",
|
||||
sa.Column("job_id", sa.Uuid(), nullable=False),
|
||||
sa.Column("file_path", sa.Text(), nullable=False),
|
||||
sa.Column("inspection_type", sa.String(64), nullable=False),
|
||||
sa.Column("status", sa.String(32), nullable=False),
|
||||
sa.Column("severity", sa.String(32), nullable=False),
|
||||
sa.Column("evidence", sa.JSON(), nullable=False, server_default=sa.text("'{}'")),
|
||||
sa.Column("observed_at", sa.DateTime(timezone=True), nullable=False, server_default=sa.func.now()),
|
||||
sa.Column("id", sa.Uuid(), primary_key=True),
|
||||
sa.ForeignKeyConstraint(["job_id"], ["artifact_jobs.id"], ondelete="RESTRICT"),
|
||||
)
|
||||
op.create_index("ix_artifact_inspections_job_id", "artifact_inspections", ["job_id"])
|
||||
op.create_table(
|
||||
"artifact_set_members",
|
||||
sa.Column("artifact_set_id", sa.Uuid(), nullable=False),
|
||||
sa.Column("artifact_id", sa.Uuid(), nullable=False),
|
||||
sa.Column("ordinal", sa.Integer(), nullable=False),
|
||||
sa.Column("required", sa.Boolean(), nullable=False, server_default=sa.true()),
|
||||
sa.ForeignKeyConstraint(["artifact_set_id"], ["artifact_sets.id"], ondelete="RESTRICT"),
|
||||
sa.ForeignKeyConstraint(["artifact_id"], ["model_artifacts.id"], ondelete="RESTRICT"),
|
||||
sa.PrimaryKeyConstraint("artifact_set_id", "artifact_id"),
|
||||
)
|
||||
|
||||
|
||||
def downgrade() -> None:
|
||||
for table in (
|
||||
"artifact_set_members",
|
||||
"artifact_inspections",
|
||||
"artifact_job_attempts",
|
||||
"artifact_jobs",
|
||||
"download_plan_files",
|
||||
"download_plans",
|
||||
"artifact_sets",
|
||||
"upstream_files",
|
||||
"upstream_snapshots",
|
||||
):
|
||||
op.drop_table(table)
|
||||
op.drop_column("storage_roots", "agent_path")
|
||||
@@ -0,0 +1,221 @@
|
||||
"""M4 runtime compatibility and runtime plane.
|
||||
|
||||
Revision ID: 20260825_0006
|
||||
Revises: 20260825_0005
|
||||
"""
|
||||
|
||||
import sqlalchemy as sa
|
||||
|
||||
from alembic import op
|
||||
|
||||
revision = "20260825_0006"
|
||||
down_revision = "20260825_0005"
|
||||
branch_labels = None
|
||||
depends_on = None
|
||||
|
||||
|
||||
def _timestamps() -> list[sa.Column]:
|
||||
return [
|
||||
sa.Column("created_at", sa.DateTime(timezone=True), server_default=sa.func.now()),
|
||||
sa.Column("updated_at", sa.DateTime(timezone=True), server_default=sa.func.now()),
|
||||
]
|
||||
|
||||
|
||||
def upgrade() -> None:
|
||||
op.create_table(
|
||||
"runtime_environments",
|
||||
sa.Column("name", sa.String(255), nullable=False),
|
||||
sa.Column("adapter", sa.String(64), nullable=False),
|
||||
sa.Column("runtime_version", sa.String(128), nullable=False),
|
||||
sa.Column("image_repository", sa.String(255), nullable=False),
|
||||
sa.Column("image_digest", sa.String(71), nullable=False),
|
||||
sa.Column("python_version", sa.String(64), nullable=False),
|
||||
sa.Column("cuda_runtime_version", sa.String(64)),
|
||||
sa.Column("package_versions", sa.JSON(), nullable=False, server_default=sa.text("'{}'")),
|
||||
sa.Column("supported_model_types", sa.JSON(), nullable=False, server_default=sa.text("'[]'")),
|
||||
sa.Column("supported_formats", sa.JSON(), nullable=False, server_default=sa.text("'[]'")),
|
||||
sa.Column("supported_modalities", sa.JSON(), nullable=False, server_default=sa.text("'[]'")),
|
||||
sa.Column("network_policy", sa.String(64), nullable=False),
|
||||
sa.Column("fingerprint", sa.String(64), nullable=False),
|
||||
sa.Column("immutable_at", sa.DateTime(timezone=True), nullable=False),
|
||||
sa.Column("created_at", sa.DateTime(timezone=True), server_default=sa.func.now()),
|
||||
sa.Column("id", sa.Uuid(), primary_key=True),
|
||||
sa.UniqueConstraint("fingerprint", name="uq_runtime_environment_fingerprint"),
|
||||
)
|
||||
op.create_index("ix_runtime_environments_adapter", "runtime_environments", ["adapter"])
|
||||
for column in (
|
||||
sa.Column("runtime_environment_id", sa.Uuid()),
|
||||
sa.Column("artifact_set_id", sa.Uuid()),
|
||||
sa.Column("version", sa.Integer(), nullable=False, server_default="1"),
|
||||
sa.Column("dtype", sa.String(32)),
|
||||
sa.Column("modality", sa.String(32)),
|
||||
sa.Column("device_policy", sa.String(32), nullable=False, server_default="cuda_required"),
|
||||
sa.Column("health_contract", sa.JSON(), nullable=False, server_default=sa.text("'{}'")),
|
||||
sa.Column("profile_config", sa.JSON(), nullable=False, server_default=sa.text("'{}'")),
|
||||
):
|
||||
op.add_column("runtime_profiles", column)
|
||||
op.create_foreign_key(
|
||||
"fk_runtime_profiles_environment",
|
||||
"runtime_profiles",
|
||||
"runtime_environments",
|
||||
["runtime_environment_id"],
|
||||
["id"],
|
||||
ondelete="RESTRICT",
|
||||
)
|
||||
op.create_foreign_key(
|
||||
"fk_runtime_profiles_artifact_set",
|
||||
"runtime_profiles",
|
||||
"artifact_sets",
|
||||
["artifact_set_id"],
|
||||
["id"],
|
||||
ondelete="RESTRICT",
|
||||
)
|
||||
op.create_index("ix_runtime_profiles_runtime_environment_id", "runtime_profiles", ["runtime_environment_id"])
|
||||
op.create_index("ix_runtime_profiles_artifact_set_id", "runtime_profiles", ["artifact_set_id"])
|
||||
|
||||
op.create_table(
|
||||
"runtime_compatibility_assessments",
|
||||
sa.Column("artifact_set_id", sa.Uuid(), nullable=False),
|
||||
sa.Column("runtime_profile_id", sa.Uuid(), nullable=False),
|
||||
sa.Column("compute_node_id", sa.Uuid(), nullable=False),
|
||||
sa.Column("adapter", sa.String(64), nullable=False),
|
||||
sa.Column("runtime_version", sa.String(128), nullable=False),
|
||||
sa.Column("status", sa.String(32), nullable=False),
|
||||
sa.Column("static_result", sa.JSON(), nullable=False, server_default=sa.text("'{}'")),
|
||||
sa.Column("evidence", sa.JSON(), nullable=False, server_default=sa.text("'{}'")),
|
||||
sa.Column("blockers", sa.JSON(), nullable=False, server_default=sa.text("'[]'")),
|
||||
sa.Column("warnings", sa.JSON(), nullable=False, server_default=sa.text("'[]'")),
|
||||
sa.Column("required_approvals", sa.JSON(), nullable=False, server_default=sa.text("'[]'")),
|
||||
sa.Column("hardware_facts", sa.JSON(), nullable=False, server_default=sa.text("'{}'")),
|
||||
sa.Column("artifact_facts", sa.JSON(), nullable=False, server_default=sa.text("'{}'")),
|
||||
sa.Column("environment_fingerprint", sa.String(64), nullable=False),
|
||||
sa.Column("stale", sa.Boolean(), nullable=False, server_default=sa.false()),
|
||||
sa.Column("stale_reason", sa.Text()),
|
||||
sa.Column("created_at", sa.DateTime(timezone=True), server_default=sa.func.now()),
|
||||
sa.Column("id", sa.Uuid(), primary_key=True),
|
||||
sa.ForeignKeyConstraint(["artifact_set_id"], ["artifact_sets.id"], ondelete="RESTRICT"),
|
||||
sa.ForeignKeyConstraint(["runtime_profile_id"], ["runtime_profiles.id"], ondelete="RESTRICT"),
|
||||
sa.ForeignKeyConstraint(["compute_node_id"], ["compute_nodes.id"], ondelete="RESTRICT"),
|
||||
)
|
||||
for column in ("artifact_set_id", "runtime_profile_id", "compute_node_id", "status", "environment_fingerprint"):
|
||||
op.create_index(f"ix_runtime_compatibility_assessments_{column}", "runtime_compatibility_assessments", [column])
|
||||
|
||||
op.create_table(
|
||||
"execution_approvals",
|
||||
sa.Column("artifact_set_id", sa.Uuid(), nullable=False),
|
||||
sa.Column("scope", sa.String(32), nullable=False),
|
||||
sa.Column("status", sa.String(32), nullable=False),
|
||||
sa.Column("evidence_fingerprint", sa.String(64), nullable=False),
|
||||
sa.Column("reason", sa.Text(), nullable=False),
|
||||
sa.Column("approved_by", sa.String(255), nullable=False),
|
||||
sa.Column("approved_at", sa.DateTime(timezone=True), server_default=sa.func.now()),
|
||||
sa.Column("expires_at", sa.DateTime(timezone=True)),
|
||||
sa.Column("revoked_at", sa.DateTime(timezone=True)),
|
||||
sa.Column("id", sa.Uuid(), primary_key=True),
|
||||
sa.ForeignKeyConstraint(["artifact_set_id"], ["artifact_sets.id"], ondelete="RESTRICT"),
|
||||
)
|
||||
op.create_index("ix_execution_approvals_artifact_set_id", "execution_approvals", ["artifact_set_id"])
|
||||
op.create_index("ix_execution_approvals_status", "execution_approvals", ["status"])
|
||||
|
||||
op.create_table(
|
||||
"runtime_probes",
|
||||
sa.Column("artifact_set_id", sa.Uuid(), nullable=False),
|
||||
sa.Column("runtime_profile_id", sa.Uuid(), nullable=False),
|
||||
sa.Column("compute_node_id", sa.Uuid(), nullable=False),
|
||||
sa.Column("compatibility_assessment_id", sa.Uuid(), nullable=False),
|
||||
sa.Column("execution_approval_id", sa.Uuid(), nullable=False),
|
||||
sa.Column("status", sa.String(32), nullable=False, server_default="queued"),
|
||||
sa.Column("phase", sa.String(64)),
|
||||
sa.Column("probe_input", sa.Text(), nullable=False),
|
||||
sa.Column("idempotency_key", sa.String(64), nullable=False),
|
||||
sa.Column("attempt_count", sa.Integer(), nullable=False, server_default="0"),
|
||||
sa.Column("lease_token_hash", sa.String(64)),
|
||||
sa.Column("lease_expires_at", sa.DateTime(timezone=True)),
|
||||
sa.Column("cancel_requested", sa.Boolean(), nullable=False, server_default=sa.false()),
|
||||
sa.Column("load_result", sa.JSON(), nullable=False, server_default=sa.text("'{}'")),
|
||||
sa.Column("health_result", sa.JSON(), nullable=False, server_default=sa.text("'{}'")),
|
||||
sa.Column("inference_result", sa.JSON(), nullable=False, server_default=sa.text("'{}'")),
|
||||
sa.Column("unload_result", sa.JSON(), nullable=False, server_default=sa.text("'{}'")),
|
||||
sa.Column("measured_resources", sa.JSON(), nullable=False, server_default=sa.text("'{}'")),
|
||||
sa.Column("runtime_facts", sa.JSON(), nullable=False, server_default=sa.text("'{}'")),
|
||||
sa.Column("environment_fingerprint", sa.String(64), nullable=False),
|
||||
sa.Column("failure_code", sa.String(64)),
|
||||
sa.Column("failure_message", sa.Text()),
|
||||
sa.Column("started_at", sa.DateTime(timezone=True)),
|
||||
sa.Column("finished_at", sa.DateTime(timezone=True)),
|
||||
sa.Column("id", sa.Uuid(), primary_key=True),
|
||||
*_timestamps(),
|
||||
sa.ForeignKeyConstraint(["artifact_set_id"], ["artifact_sets.id"], ondelete="RESTRICT"),
|
||||
sa.ForeignKeyConstraint(["runtime_profile_id"], ["runtime_profiles.id"], ondelete="RESTRICT"),
|
||||
sa.ForeignKeyConstraint(["compute_node_id"], ["compute_nodes.id"], ondelete="RESTRICT"),
|
||||
sa.ForeignKeyConstraint(["compatibility_assessment_id"], ["runtime_compatibility_assessments.id"], ondelete="RESTRICT"),
|
||||
sa.ForeignKeyConstraint(["execution_approval_id"], ["execution_approvals.id"], ondelete="RESTRICT"),
|
||||
sa.UniqueConstraint("idempotency_key", name="uq_runtime_probe_idempotency"),
|
||||
)
|
||||
for column in ("artifact_set_id", "runtime_profile_id", "compute_node_id", "compatibility_assessment_id", "execution_approval_id", "status"):
|
||||
op.create_index(f"ix_runtime_probes_{column}", "runtime_probes", [column])
|
||||
|
||||
op.create_table(
|
||||
"runtime_probe_metrics",
|
||||
sa.Column("runtime_probe_id", sa.Uuid(), nullable=False),
|
||||
sa.Column("phase", sa.String(64), nullable=False),
|
||||
sa.Column("measurement_type", sa.String(64), nullable=False),
|
||||
sa.Column("source", sa.String(64), nullable=False),
|
||||
sa.Column("values", sa.JSON(), nullable=False, server_default=sa.text("'{}'")),
|
||||
sa.Column("observed_at", sa.DateTime(timezone=True), nullable=False),
|
||||
sa.Column("id", sa.Uuid(), primary_key=True),
|
||||
sa.ForeignKeyConstraint(["runtime_probe_id"], ["runtime_probes.id"], ondelete="RESTRICT"),
|
||||
)
|
||||
op.create_index("ix_runtime_probe_metrics_runtime_probe_id", "runtime_probe_metrics", ["runtime_probe_id"])
|
||||
|
||||
op.create_table(
|
||||
"deployment_candidates",
|
||||
sa.Column("artifact_set_id", sa.Uuid(), nullable=False),
|
||||
sa.Column("runtime_profile_id", sa.Uuid(), nullable=False),
|
||||
sa.Column("compute_node_id", sa.Uuid(), nullable=False),
|
||||
sa.Column("compatibility_assessment_id", sa.Uuid(), nullable=False),
|
||||
sa.Column("runtime_probe_id", sa.Uuid(), nullable=False),
|
||||
sa.Column("channel", sa.String(32), nullable=False, server_default="lab"),
|
||||
sa.Column("status", sa.String(32), nullable=False, server_default="lab_ready"),
|
||||
sa.Column("production", sa.Boolean(), nullable=False, server_default=sa.false()),
|
||||
sa.Column("health_contract", sa.JSON(), nullable=False, server_default=sa.text("'{}'")),
|
||||
sa.Column("measured_resources", sa.JSON(), nullable=False, server_default=sa.text("'{}'")),
|
||||
sa.Column("created_at", sa.DateTime(timezone=True), server_default=sa.func.now()),
|
||||
sa.Column("id", sa.Uuid(), primary_key=True),
|
||||
sa.ForeignKeyConstraint(["artifact_set_id"], ["artifact_sets.id"], ondelete="RESTRICT"),
|
||||
sa.ForeignKeyConstraint(["runtime_profile_id"], ["runtime_profiles.id"], ondelete="RESTRICT"),
|
||||
sa.ForeignKeyConstraint(["compute_node_id"], ["compute_nodes.id"], ondelete="RESTRICT"),
|
||||
sa.ForeignKeyConstraint(["compatibility_assessment_id"], ["runtime_compatibility_assessments.id"], ondelete="RESTRICT"),
|
||||
sa.ForeignKeyConstraint(["runtime_probe_id"], ["runtime_probes.id"], ondelete="RESTRICT"),
|
||||
sa.UniqueConstraint("runtime_probe_id", name="uq_deployment_candidate_probe"),
|
||||
)
|
||||
for column in ("artifact_set_id", "runtime_profile_id", "compute_node_id", "runtime_probe_id", "status"):
|
||||
op.create_index(f"ix_deployment_candidates_{column}", "deployment_candidates", [column])
|
||||
|
||||
|
||||
def downgrade() -> None:
|
||||
for table in (
|
||||
"deployment_candidates",
|
||||
"runtime_probe_metrics",
|
||||
"runtime_probes",
|
||||
"execution_approvals",
|
||||
"runtime_compatibility_assessments",
|
||||
):
|
||||
op.drop_table(table)
|
||||
op.drop_index("ix_runtime_profiles_artifact_set_id", table_name="runtime_profiles")
|
||||
op.drop_index("ix_runtime_profiles_runtime_environment_id", table_name="runtime_profiles")
|
||||
op.drop_constraint("fk_runtime_profiles_artifact_set", "runtime_profiles", type_="foreignkey")
|
||||
op.drop_constraint("fk_runtime_profiles_environment", "runtime_profiles", type_="foreignkey")
|
||||
for column in (
|
||||
"profile_config",
|
||||
"health_contract",
|
||||
"device_policy",
|
||||
"modality",
|
||||
"dtype",
|
||||
"version",
|
||||
"artifact_set_id",
|
||||
"runtime_environment_id",
|
||||
):
|
||||
op.drop_column("runtime_profiles", column)
|
||||
op.drop_table("runtime_environments")
|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
"""M4 bounded runtime log reference.
|
||||
|
||||
Revision ID: 20260825_0007
|
||||
Revises: 20260825_0006
|
||||
"""
|
||||
|
||||
from collections.abc import Sequence
|
||||
|
||||
import sqlalchemy as sa
|
||||
|
||||
from alembic import op
|
||||
|
||||
revision: str = "20260825_0007"
|
||||
down_revision: str | None = "20260825_0006"
|
||||
branch_labels: str | Sequence[str] | None = None
|
||||
depends_on: str | Sequence[str] | None = None
|
||||
|
||||
|
||||
def upgrade() -> None:
|
||||
op.add_column("runtime_probes", sa.Column("logs_reference", sa.String(512)))
|
||||
op.execute(
|
||||
"UPDATE runtime_probes SET logs_reference = "
|
||||
"'runtime-worker://historical-probe/' || CAST(id AS VARCHAR)"
|
||||
)
|
||||
|
||||
|
||||
def downgrade() -> None:
|
||||
op.drop_column("runtime_probes", "logs_reference")
|
||||
@@ -0,0 +1,382 @@
|
||||
"""M5 capability serving, gateway and GPU scheduling.
|
||||
|
||||
Revision ID: 20260825_0008
|
||||
Revises: 20260825_0007
|
||||
"""
|
||||
|
||||
import sqlalchemy as sa
|
||||
|
||||
from alembic import op
|
||||
|
||||
revision = "20260825_0008"
|
||||
down_revision = "20260825_0007"
|
||||
branch_labels = None
|
||||
depends_on = None
|
||||
|
||||
|
||||
def _identity_columns() -> list[sa.Column]:
|
||||
return [sa.Column("id", sa.Uuid(), primary_key=True)]
|
||||
|
||||
|
||||
def _timestamps() -> list[sa.Column]:
|
||||
return [
|
||||
sa.Column("created_at", sa.DateTime(timezone=True), server_default=sa.func.now()),
|
||||
sa.Column("updated_at", sa.DateTime(timezone=True), server_default=sa.func.now()),
|
||||
]
|
||||
|
||||
|
||||
def upgrade() -> None:
|
||||
op.create_table(
|
||||
"production_execution_approvals",
|
||||
*_identity_columns(),
|
||||
sa.Column("deployment_candidate_id", sa.Uuid(), nullable=False),
|
||||
sa.Column("capability_contract_id", sa.Uuid(), nullable=False),
|
||||
sa.Column("artifact_set_id", sa.Uuid(), nullable=False),
|
||||
sa.Column("runtime_profile_id", sa.Uuid(), nullable=False),
|
||||
sa.Column("compute_node_id", sa.Uuid(), nullable=False),
|
||||
sa.Column("deployment_config", sa.JSON(), nullable=False),
|
||||
sa.Column("supply_chain_evidence", sa.JSON(), nullable=False),
|
||||
sa.Column("evidence_fingerprint", sa.String(64), nullable=False),
|
||||
sa.Column("status", sa.String(32), nullable=False, server_default="approved"),
|
||||
sa.Column("approved_by", sa.String(255), nullable=False),
|
||||
sa.Column("reason", sa.Text(), nullable=False),
|
||||
sa.Column("approved_at", sa.DateTime(timezone=True), server_default=sa.func.now()),
|
||||
sa.Column("immutable_at", sa.DateTime(timezone=True), server_default=sa.func.now()),
|
||||
sa.Column("revoked_at", sa.DateTime(timezone=True)),
|
||||
sa.ForeignKeyConstraint(["deployment_candidate_id"], ["deployment_candidates.id"]),
|
||||
sa.ForeignKeyConstraint(["capability_contract_id"], ["capability_contracts.id"]),
|
||||
sa.ForeignKeyConstraint(["artifact_set_id"], ["artifact_sets.id"]),
|
||||
sa.ForeignKeyConstraint(["runtime_profile_id"], ["runtime_profiles.id"]),
|
||||
sa.ForeignKeyConstraint(["compute_node_id"], ["compute_nodes.id"]),
|
||||
sa.UniqueConstraint("evidence_fingerprint", name="uq_production_approval_fingerprint"),
|
||||
)
|
||||
for column in (
|
||||
"deployment_candidate_id",
|
||||
"capability_contract_id",
|
||||
"artifact_set_id",
|
||||
"runtime_profile_id",
|
||||
"compute_node_id",
|
||||
"status",
|
||||
):
|
||||
op.create_index(
|
||||
f"ix_production_execution_approvals_{column}",
|
||||
"production_execution_approvals",
|
||||
[column],
|
||||
)
|
||||
|
||||
op.create_table(
|
||||
"embedding_spaces",
|
||||
*_identity_columns(),
|
||||
sa.Column("capability_contract_id", sa.Uuid(), nullable=False),
|
||||
sa.Column("artifact_set_id", sa.Uuid(), nullable=False),
|
||||
sa.Column("runtime_profile_id", sa.Uuid(), nullable=False),
|
||||
sa.Column("identity_digest", sa.String(64), nullable=False),
|
||||
sa.Column("dimension", sa.Integer(), nullable=False),
|
||||
sa.Column("normalized", sa.Boolean(), nullable=False),
|
||||
sa.Column("migration_class", sa.String(32), nullable=False),
|
||||
sa.Column("identity_facts", sa.JSON(), nullable=False),
|
||||
sa.Column("created_at", sa.DateTime(timezone=True), server_default=sa.func.now()),
|
||||
sa.Column("immutable_at", sa.DateTime(timezone=True), server_default=sa.func.now()),
|
||||
sa.ForeignKeyConstraint(["capability_contract_id"], ["capability_contracts.id"]),
|
||||
sa.ForeignKeyConstraint(["artifact_set_id"], ["artifact_sets.id"]),
|
||||
sa.ForeignKeyConstraint(["runtime_profile_id"], ["runtime_profiles.id"]),
|
||||
sa.UniqueConstraint("identity_digest", name="uq_embedding_space_digest"),
|
||||
)
|
||||
for column in ("capability_contract_id", "artifact_set_id", "runtime_profile_id"):
|
||||
op.create_index(f"ix_embedding_spaces_{column}", "embedding_spaces", [column])
|
||||
|
||||
op.create_table(
|
||||
"capability_deployments",
|
||||
*_identity_columns(),
|
||||
*_timestamps(),
|
||||
sa.Column("capability_contract_id", sa.Uuid(), nullable=False),
|
||||
sa.Column("deployment_candidate_id", sa.Uuid(), nullable=False),
|
||||
sa.Column("production_approval_id", sa.Uuid(), nullable=False),
|
||||
sa.Column("embedding_space_id", sa.Uuid(), nullable=False),
|
||||
sa.Column("artifact_set_id", sa.Uuid(), nullable=False),
|
||||
sa.Column("runtime_profile_id", sa.Uuid(), nullable=False),
|
||||
sa.Column("compute_node_id", sa.Uuid(), nullable=False),
|
||||
sa.Column("accelerator_id", sa.Uuid(), nullable=False),
|
||||
sa.Column("channel", sa.String(32), nullable=False, server_default="stable"),
|
||||
sa.Column("status", sa.String(32), nullable=False, server_default="approved"),
|
||||
sa.Column("production", sa.Boolean(), nullable=False, server_default=sa.false()),
|
||||
sa.Column("health_status", sa.String(32), nullable=False, server_default="ready_on_demand"),
|
||||
sa.Column("routing_weight", sa.Integer(), nullable=False, server_default="100"),
|
||||
sa.Column("fallback_policy", sa.JSON(), nullable=False),
|
||||
sa.Column("residency_policy", sa.String(32), nullable=False),
|
||||
sa.Column("keep_warm_seconds", sa.Integer(), nullable=False),
|
||||
sa.Column("max_concurrency", sa.Integer(), nullable=False),
|
||||
sa.Column("max_queue_depth", sa.Integer(), nullable=False),
|
||||
sa.Column("config_fingerprint", sa.String(64), nullable=False),
|
||||
sa.Column("provenance", sa.JSON(), nullable=False),
|
||||
sa.Column("rollback_policy", sa.JSON(), nullable=False),
|
||||
sa.Column("promoted_at", sa.DateTime(timezone=True)),
|
||||
sa.Column("draining_at", sa.DateTime(timezone=True)),
|
||||
sa.Column("deprecated_at", sa.DateTime(timezone=True)),
|
||||
sa.ForeignKeyConstraint(["capability_contract_id"], ["capability_contracts.id"]),
|
||||
sa.ForeignKeyConstraint(["deployment_candidate_id"], ["deployment_candidates.id"]),
|
||||
sa.ForeignKeyConstraint(["production_approval_id"], ["production_execution_approvals.id"]),
|
||||
sa.ForeignKeyConstraint(["embedding_space_id"], ["embedding_spaces.id"]),
|
||||
sa.ForeignKeyConstraint(["artifact_set_id"], ["artifact_sets.id"]),
|
||||
sa.ForeignKeyConstraint(["runtime_profile_id"], ["runtime_profiles.id"]),
|
||||
sa.ForeignKeyConstraint(["compute_node_id"], ["compute_nodes.id"]),
|
||||
sa.ForeignKeyConstraint(["accelerator_id"], ["accelerators.id"]),
|
||||
sa.UniqueConstraint("config_fingerprint", name="uq_capability_deployment_fingerprint"),
|
||||
)
|
||||
for column in (
|
||||
"capability_contract_id",
|
||||
"deployment_candidate_id",
|
||||
"production_approval_id",
|
||||
"embedding_space_id",
|
||||
"artifact_set_id",
|
||||
"runtime_profile_id",
|
||||
"compute_node_id",
|
||||
"accelerator_id",
|
||||
"channel",
|
||||
"status",
|
||||
):
|
||||
op.create_index(f"ix_capability_deployments_{column}", "capability_deployments", [column])
|
||||
op.create_index(
|
||||
"ix_capability_deployments_contract_channel_status",
|
||||
"capability_deployments",
|
||||
["capability_contract_id", "channel", "status"],
|
||||
)
|
||||
|
||||
op.create_table(
|
||||
"capability_resource_envelopes",
|
||||
*_identity_columns(),
|
||||
*_timestamps(),
|
||||
sa.Column("capability_deployment_id", sa.Uuid(), nullable=False),
|
||||
sa.Column("runtime_probe_id", sa.Uuid(), nullable=False),
|
||||
sa.Column("accelerator_kind", sa.String(255), nullable=False),
|
||||
sa.Column("accelerator_uuid", sa.String(255), nullable=False),
|
||||
sa.Column("environment_fingerprint", sa.String(64), nullable=False),
|
||||
sa.Column("concurrency", sa.Integer(), nullable=False),
|
||||
sa.Column("batch_size", sa.Integer(), nullable=False),
|
||||
sa.Column("max_sequence_length", sa.Integer(), nullable=False),
|
||||
sa.Column("baseline_vram_bytes", sa.BigInteger(), nullable=False),
|
||||
sa.Column("resident_vram_bytes", sa.BigInteger(), nullable=False),
|
||||
sa.Column("peak_vram_bytes", sa.BigInteger(), nullable=False),
|
||||
sa.Column("required_vram_bytes", sa.BigInteger(), nullable=False),
|
||||
sa.Column("cold_load_time_ms", sa.Float(), nullable=False),
|
||||
sa.Column("inference_latency_ms", sa.Float(), nullable=False),
|
||||
sa.Column("evidence", sa.JSON(), nullable=False),
|
||||
sa.Column("stale", sa.Boolean(), nullable=False, server_default=sa.false()),
|
||||
sa.Column("stale_reason", sa.Text()),
|
||||
sa.Column("immutable_at", sa.DateTime(timezone=True), server_default=sa.func.now()),
|
||||
sa.ForeignKeyConstraint(["capability_deployment_id"], ["capability_deployments.id"]),
|
||||
sa.ForeignKeyConstraint(["runtime_probe_id"], ["runtime_probes.id"]),
|
||||
sa.UniqueConstraint("capability_deployment_id", name="uq_capability_resource_envelope"),
|
||||
)
|
||||
op.create_index(
|
||||
"ix_capability_resource_envelopes_capability_deployment_id",
|
||||
"capability_resource_envelopes",
|
||||
["capability_deployment_id"],
|
||||
)
|
||||
op.create_index(
|
||||
"ix_capability_resource_envelopes_runtime_probe_id",
|
||||
"capability_resource_envelopes",
|
||||
["runtime_probe_id"],
|
||||
)
|
||||
op.create_index(
|
||||
"ix_capability_resource_envelopes_environment_fingerprint",
|
||||
"capability_resource_envelopes",
|
||||
["environment_fingerprint"],
|
||||
)
|
||||
|
||||
op.create_table(
|
||||
"service_clients",
|
||||
*_identity_columns(),
|
||||
*_timestamps(),
|
||||
sa.Column("name", sa.String(255), nullable=False, unique=True),
|
||||
sa.Column("status", sa.String(32), nullable=False, server_default="active"),
|
||||
sa.Column("allowed_capabilities", sa.JSON(), nullable=False),
|
||||
sa.Column("requests_per_minute", sa.Integer(), nullable=False),
|
||||
sa.Column("max_concurrent_requests", sa.Integer(), nullable=False),
|
||||
sa.Column("last_used_at", sa.DateTime(timezone=True)),
|
||||
sa.Column("disabled_at", sa.DateTime(timezone=True)),
|
||||
)
|
||||
op.create_index("ix_service_clients_status", "service_clients", ["status"])
|
||||
op.create_table(
|
||||
"service_credentials",
|
||||
*_identity_columns(),
|
||||
sa.Column("service_client_id", sa.Uuid(), nullable=False),
|
||||
sa.Column("secret_hash", sa.String(64), nullable=False, unique=True),
|
||||
sa.Column("secret_prefix", sa.String(16), nullable=False),
|
||||
sa.Column("created_at", sa.DateTime(timezone=True), server_default=sa.func.now()),
|
||||
sa.Column("expires_at", sa.DateTime(timezone=True)),
|
||||
sa.Column("last_used_at", sa.DateTime(timezone=True)),
|
||||
sa.Column("revoked_at", sa.DateTime(timezone=True)),
|
||||
sa.ForeignKeyConstraint(["service_client_id"], ["service_clients.id"], ondelete="CASCADE"),
|
||||
)
|
||||
op.create_index(
|
||||
"ix_service_credentials_service_client_id", "service_credentials", ["service_client_id"]
|
||||
)
|
||||
op.create_index(
|
||||
"uq_active_service_client_credential",
|
||||
"service_credentials",
|
||||
["service_client_id"],
|
||||
unique=True,
|
||||
postgresql_where=sa.text("revoked_at IS NULL"),
|
||||
)
|
||||
|
||||
op.create_table(
|
||||
"residency_allocations",
|
||||
*_identity_columns(),
|
||||
*_timestamps(),
|
||||
sa.Column("capability_deployment_id", sa.Uuid(), nullable=False),
|
||||
sa.Column("compute_node_id", sa.Uuid(), nullable=False),
|
||||
sa.Column("accelerator_id", sa.Uuid(), nullable=False),
|
||||
sa.Column("state", sa.String(32), nullable=False, server_default="cold"),
|
||||
sa.Column("worker_instance_id", sa.String(255)),
|
||||
sa.Column("load_count", sa.Integer(), nullable=False, server_default="0"),
|
||||
sa.Column("active_requests", sa.Integer(), nullable=False, server_default="0"),
|
||||
sa.Column(
|
||||
"measured_resident_vram_bytes", sa.BigInteger(), nullable=False, server_default="0"
|
||||
),
|
||||
sa.Column(
|
||||
"external_baseline_vram_bytes", sa.BigInteger(), nullable=False, server_default="0"
|
||||
),
|
||||
sa.Column("health", sa.JSON(), nullable=False),
|
||||
sa.Column("resident_since", sa.DateTime(timezone=True)),
|
||||
sa.Column("last_used_at", sa.DateTime(timezone=True)),
|
||||
sa.Column("transition_started_at", sa.DateTime(timezone=True)),
|
||||
sa.Column("failure_code", sa.String(64)),
|
||||
sa.Column("failure_message", sa.Text()),
|
||||
sa.ForeignKeyConstraint(["capability_deployment_id"], ["capability_deployments.id"]),
|
||||
sa.ForeignKeyConstraint(["compute_node_id"], ["compute_nodes.id"]),
|
||||
sa.ForeignKeyConstraint(["accelerator_id"], ["accelerators.id"]),
|
||||
sa.UniqueConstraint("capability_deployment_id", name="uq_residency_deployment"),
|
||||
)
|
||||
for column in ("capability_deployment_id", "compute_node_id", "accelerator_id", "state"):
|
||||
op.create_index(f"ix_residency_allocations_{column}", "residency_allocations", [column])
|
||||
|
||||
op.create_table(
|
||||
"serving_gpu_leases",
|
||||
*_identity_columns(),
|
||||
sa.Column("accelerator_id", sa.Uuid(), nullable=False),
|
||||
sa.Column("capability_deployment_id", sa.Uuid(), nullable=False),
|
||||
sa.Column("request_id", sa.Uuid()),
|
||||
sa.Column("reserved_vram_bytes", sa.BigInteger(), nullable=False),
|
||||
sa.Column("priority", sa.String(32), nullable=False),
|
||||
sa.Column("state", sa.String(32), nullable=False, server_default="pending"),
|
||||
sa.Column("owner", sa.String(255), nullable=False),
|
||||
sa.Column("created_at", sa.DateTime(timezone=True), server_default=sa.func.now()),
|
||||
sa.Column("expires_at", sa.DateTime(timezone=True), nullable=False),
|
||||
sa.Column("acquired_at", sa.DateTime(timezone=True)),
|
||||
sa.Column("heartbeat_at", sa.DateTime(timezone=True)),
|
||||
sa.Column("released_at", sa.DateTime(timezone=True)),
|
||||
sa.Column("failure_code", sa.String(64)),
|
||||
sa.ForeignKeyConstraint(["accelerator_id"], ["accelerators.id"]),
|
||||
sa.ForeignKeyConstraint(["capability_deployment_id"], ["capability_deployments.id"]),
|
||||
)
|
||||
for column in (
|
||||
"accelerator_id",
|
||||
"capability_deployment_id",
|
||||
"request_id",
|
||||
"state",
|
||||
"expires_at",
|
||||
):
|
||||
op.create_index(f"ix_serving_gpu_leases_{column}", "serving_gpu_leases", [column])
|
||||
|
||||
op.create_table(
|
||||
"gateway_requests",
|
||||
*_identity_columns(),
|
||||
sa.Column("request_id", sa.Uuid(), nullable=False, unique=True),
|
||||
sa.Column("service_client_id", sa.Uuid()),
|
||||
sa.Column("capability_key", sa.String(128), nullable=False),
|
||||
sa.Column("capability_version", sa.Integer(), nullable=False),
|
||||
sa.Column("capability_deployment_id", sa.Uuid()),
|
||||
sa.Column("compute_node_id", sa.Uuid()),
|
||||
sa.Column("status", sa.String(32), nullable=False),
|
||||
sa.Column("priority", sa.String(32), nullable=False),
|
||||
sa.Column("input_sha256", sa.String(64), nullable=False),
|
||||
sa.Column("input_count", sa.Integer(), nullable=False),
|
||||
sa.Column("cold", sa.Boolean()),
|
||||
sa.Column("queue_time_ms", sa.Float()),
|
||||
sa.Column("load_time_ms", sa.Float()),
|
||||
sa.Column("inference_time_ms", sa.Float()),
|
||||
sa.Column("total_latency_ms", sa.Float()),
|
||||
sa.Column("failure_code", sa.String(64)),
|
||||
sa.Column("decision_evidence", sa.JSON(), nullable=False),
|
||||
sa.Column("created_at", sa.DateTime(timezone=True), server_default=sa.func.now()),
|
||||
sa.Column("finished_at", sa.DateTime(timezone=True)),
|
||||
sa.ForeignKeyConstraint(["service_client_id"], ["service_clients.id"], ondelete="SET NULL"),
|
||||
sa.ForeignKeyConstraint(
|
||||
["capability_deployment_id"], ["capability_deployments.id"], ondelete="SET NULL"
|
||||
),
|
||||
sa.ForeignKeyConstraint(["compute_node_id"], ["compute_nodes.id"], ondelete="SET NULL"),
|
||||
)
|
||||
for column in (
|
||||
"request_id",
|
||||
"service_client_id",
|
||||
"capability_key",
|
||||
"capability_deployment_id",
|
||||
"compute_node_id",
|
||||
"status",
|
||||
"created_at",
|
||||
):
|
||||
op.create_index(f"ix_gateway_requests_{column}", "gateway_requests", [column])
|
||||
op.create_index(
|
||||
"ix_gateway_requests_client_created",
|
||||
"gateway_requests",
|
||||
["service_client_id", "created_at"],
|
||||
)
|
||||
|
||||
op.create_table(
|
||||
"serving_jobs",
|
||||
*_identity_columns(),
|
||||
*_timestamps(),
|
||||
sa.Column("capability_deployment_id", sa.Uuid(), nullable=False),
|
||||
sa.Column("compute_node_id", sa.Uuid(), nullable=False),
|
||||
sa.Column("gateway_request_id", sa.Uuid()),
|
||||
sa.Column("operation", sa.String(32), nullable=False),
|
||||
sa.Column("status", sa.String(32), nullable=False, server_default="queued"),
|
||||
sa.Column("priority", sa.String(32), nullable=False),
|
||||
sa.Column("idempotency_key", sa.String(64), nullable=False),
|
||||
sa.Column("payload_reference", sa.String(128)),
|
||||
sa.Column("attempt_count", sa.Integer(), nullable=False, server_default="0"),
|
||||
sa.Column("lease_token_hash", sa.String(64)),
|
||||
sa.Column("lease_expires_at", sa.DateTime(timezone=True)),
|
||||
sa.Column("result_reference", sa.String(128)),
|
||||
sa.Column("result_summary", sa.JSON(), nullable=False),
|
||||
sa.Column("failure_code", sa.String(64)),
|
||||
sa.Column("failure_message", sa.Text()),
|
||||
sa.Column("started_at", sa.DateTime(timezone=True)),
|
||||
sa.Column("finished_at", sa.DateTime(timezone=True)),
|
||||
sa.ForeignKeyConstraint(["capability_deployment_id"], ["capability_deployments.id"]),
|
||||
sa.ForeignKeyConstraint(["compute_node_id"], ["compute_nodes.id"]),
|
||||
sa.ForeignKeyConstraint(
|
||||
["gateway_request_id"], ["gateway_requests.id"], ondelete="SET NULL"
|
||||
),
|
||||
sa.UniqueConstraint("idempotency_key", name="uq_serving_job_idempotency"),
|
||||
)
|
||||
for column in (
|
||||
"capability_deployment_id",
|
||||
"compute_node_id",
|
||||
"gateway_request_id",
|
||||
"operation",
|
||||
"status",
|
||||
):
|
||||
op.create_index(f"ix_serving_jobs_{column}", "serving_jobs", [column])
|
||||
op.create_index(
|
||||
"ix_serving_jobs_node_status_priority",
|
||||
"serving_jobs",
|
||||
["compute_node_id", "status", "priority"],
|
||||
)
|
||||
|
||||
|
||||
def downgrade() -> None:
|
||||
for table in (
|
||||
"serving_jobs",
|
||||
"gateway_requests",
|
||||
"serving_gpu_leases",
|
||||
"residency_allocations",
|
||||
"service_credentials",
|
||||
"service_clients",
|
||||
"capability_resource_envelopes",
|
||||
"capability_deployments",
|
||||
"embedding_spaces",
|
||||
"production_execution_approvals",
|
||||
):
|
||||
op.drop_table(table)
|
||||
@@ -0,0 +1,165 @@
|
||||
"""M6 project evaluation and isolated embedding migrations.
|
||||
|
||||
Revision ID: 20260825_0009
|
||||
Revises: 20260825_0008
|
||||
"""
|
||||
|
||||
import sqlalchemy as sa
|
||||
|
||||
from alembic import op
|
||||
|
||||
revision = "20260825_0009"
|
||||
down_revision = "20260825_0008"
|
||||
branch_labels = None
|
||||
depends_on = None
|
||||
|
||||
|
||||
def _id() -> sa.Column:
|
||||
return sa.Column("id", sa.Uuid(), primary_key=True)
|
||||
|
||||
|
||||
def _created() -> sa.Column:
|
||||
return sa.Column("created_at", sa.DateTime(timezone=True), server_default=sa.func.now())
|
||||
|
||||
|
||||
def upgrade() -> None:
|
||||
op.add_column(
|
||||
"service_clients",
|
||||
sa.Column("workload_priority", sa.String(32), nullable=False, server_default="production"),
|
||||
)
|
||||
op.create_table(
|
||||
"project_evaluation_bindings", _id(), _created(),
|
||||
sa.Column("updated_at", sa.DateTime(timezone=True), server_default=sa.func.now()),
|
||||
sa.Column("project_id", sa.Uuid(), nullable=False),
|
||||
sa.Column("adapter_kind", sa.String(64), nullable=False),
|
||||
sa.Column("endpoint", sa.Text(), nullable=False),
|
||||
sa.Column("current_target", sa.String(128), nullable=False),
|
||||
sa.Column("shadow_target", sa.String(128)),
|
||||
sa.Column("enabled", sa.Boolean(), nullable=False, server_default=sa.true()),
|
||||
sa.Column("configuration", sa.JSON(), nullable=False),
|
||||
sa.ForeignKeyConstraint(["project_id"], ["projects.id"], ondelete="RESTRICT"),
|
||||
sa.UniqueConstraint("project_id", name="uq_project_evaluation_binding"),
|
||||
)
|
||||
op.create_index("ix_project_evaluation_bindings_project_id", "project_evaluation_bindings", ["project_id"])
|
||||
|
||||
op.create_table(
|
||||
"embedding_migrations", _id(), _created(),
|
||||
sa.Column("updated_at", sa.DateTime(timezone=True), server_default=sa.func.now()),
|
||||
sa.Column("project_id", sa.Uuid(), nullable=False),
|
||||
sa.Column("source_embedding_space", sa.String(255), nullable=False),
|
||||
sa.Column("target_embedding_space_id", sa.Uuid(), nullable=False),
|
||||
sa.Column("source_index_ref", sa.Text(), nullable=False),
|
||||
sa.Column("target_index_ref", sa.Text(), nullable=False, unique=True),
|
||||
sa.Column("corpus_revision", sa.String(128), nullable=False),
|
||||
sa.Column("status", sa.String(32), nullable=False, server_default="planned"),
|
||||
sa.Column("total_chunks", sa.Integer(), nullable=False, server_default="0"),
|
||||
sa.Column("completed_chunks", sa.Integer(), nullable=False, server_default="0"),
|
||||
sa.Column("failed_chunks", sa.Integer(), nullable=False, server_default="0"),
|
||||
sa.Column("retried_chunks", sa.Integer(), nullable=False, server_default="0"),
|
||||
sa.Column("batch_size", sa.Integer(), nullable=False),
|
||||
sa.Column("concurrency", sa.Integer(), nullable=False),
|
||||
sa.Column("priority", sa.String(32), nullable=False, server_default="background"),
|
||||
sa.Column("preflight_evidence", sa.JSON(), nullable=False),
|
||||
sa.Column("progress_evidence", sa.JSON(), nullable=False),
|
||||
sa.Column("validation_evidence", sa.JSON(), nullable=False),
|
||||
sa.Column("operational_metrics", sa.JSON(), nullable=False),
|
||||
sa.Column("evaluation_eligibility", sa.Boolean(), nullable=False, server_default=sa.false()),
|
||||
sa.Column("cancel_requested", sa.Boolean(), nullable=False, server_default=sa.false()),
|
||||
sa.Column("failure_code", sa.String(64)), sa.Column("failure_message", sa.Text()),
|
||||
sa.Column("started_at", sa.DateTime(timezone=True)),
|
||||
sa.Column("finished_at", sa.DateTime(timezone=True)),
|
||||
sa.ForeignKeyConstraint(["project_id"], ["projects.id"], ondelete="RESTRICT"),
|
||||
sa.ForeignKeyConstraint(["target_embedding_space_id"], ["embedding_spaces.id"], ondelete="RESTRICT"),
|
||||
)
|
||||
for column in ("project_id", "target_embedding_space_id", "status"):
|
||||
op.create_index(f"ix_embedding_migrations_{column}", "embedding_migrations", [column])
|
||||
|
||||
op.create_table(
|
||||
"evaluation_suites", _id(), _created(),
|
||||
sa.Column("updated_at", sa.DateTime(timezone=True), server_default=sa.func.now()),
|
||||
sa.Column("project_id", sa.Uuid(), nullable=False), sa.Column("key", sa.String(128), nullable=False),
|
||||
sa.Column("name", sa.String(255), nullable=False), sa.Column("description", sa.Text(), nullable=False),
|
||||
sa.ForeignKeyConstraint(["project_id"], ["projects.id"], ondelete="RESTRICT"),
|
||||
sa.UniqueConstraint("project_id", "key", name="uq_evaluation_suite_key"),
|
||||
)
|
||||
op.create_index("ix_evaluation_suites_project_id", "evaluation_suites", ["project_id"])
|
||||
op.create_table(
|
||||
"evaluation_suite_revisions", _id(), _created(),
|
||||
sa.Column("suite_id", sa.Uuid(), nullable=False), sa.Column("revision", sa.String(128), nullable=False),
|
||||
sa.Column("dataset_revision", sa.String(128), nullable=False),
|
||||
sa.Column("definition_digest", sa.String(64), nullable=False), sa.Column("metrics", sa.JSON(), nullable=False),
|
||||
sa.Column("top_k", sa.Integer(), nullable=False), sa.Column("retrieval_settings", sa.JSON(), nullable=False),
|
||||
sa.Column("thresholds", sa.JSON(), nullable=False),
|
||||
sa.Column("immutable_at", sa.DateTime(timezone=True), server_default=sa.func.now()),
|
||||
sa.ForeignKeyConstraint(["suite_id"], ["evaluation_suites.id"], ondelete="RESTRICT"),
|
||||
sa.UniqueConstraint("suite_id", "revision", name="uq_evaluation_suite_revision"),
|
||||
sa.UniqueConstraint("definition_digest", name="uq_evaluation_revision_digest"),
|
||||
)
|
||||
op.create_index("ix_evaluation_suite_revisions_suite_id", "evaluation_suite_revisions", ["suite_id"])
|
||||
op.create_table(
|
||||
"evaluation_cases", _id(), _created(),
|
||||
sa.Column("suite_revision_id", sa.Uuid(), nullable=False), sa.Column("case_key", sa.String(128), nullable=False),
|
||||
sa.Column("query", sa.Text(), nullable=False), sa.Column("relevant_chunk_ids", sa.JSON(), nullable=False),
|
||||
sa.Column("relevant_document_ids", sa.JSON(), nullable=False), sa.Column("relevance_grades", sa.JSON(), nullable=False),
|
||||
sa.Column("label_provenance", sa.JSON(), nullable=False), sa.Column("critical", sa.Boolean(), nullable=False),
|
||||
sa.Column("review_status", sa.String(32), nullable=False),
|
||||
sa.Column("immutable_at", sa.DateTime(timezone=True), server_default=sa.func.now()),
|
||||
sa.ForeignKeyConstraint(["suite_revision_id"], ["evaluation_suite_revisions.id"], ondelete="RESTRICT"),
|
||||
sa.UniqueConstraint("suite_revision_id", "case_key", name="uq_evaluation_case_key"),
|
||||
)
|
||||
op.create_index("ix_evaluation_cases_suite_revision_id", "evaluation_cases", ["suite_revision_id"])
|
||||
op.create_index("ix_evaluation_cases_critical", "evaluation_cases", ["critical"])
|
||||
op.create_table(
|
||||
"evaluation_runs", _id(), _created(),
|
||||
sa.Column("project_id", sa.Uuid(), nullable=False), sa.Column("suite_revision_id", sa.Uuid(), nullable=False),
|
||||
sa.Column("target_kind", sa.String(32), nullable=False), sa.Column("target_index_ref", sa.Text(), nullable=False),
|
||||
sa.Column("embedding_space_ref", sa.String(255), nullable=False), sa.Column("capability_deployment_id", sa.Uuid()),
|
||||
sa.Column("status", sa.String(32), nullable=False), sa.Column("corpus_revision", sa.String(128), nullable=False),
|
||||
sa.Column("retrieval_config_digest", sa.String(64), nullable=False),
|
||||
sa.Column("environment_fingerprint", sa.JSON(), nullable=False), sa.Column("environment_digest", sa.String(64), nullable=False),
|
||||
sa.Column("expected_cases", sa.Integer(), nullable=False), sa.Column("completed_cases", sa.Integer(), nullable=False),
|
||||
sa.Column("error_count", sa.Integer(), nullable=False), sa.Column("aggregate_metrics", sa.JSON(), nullable=False),
|
||||
sa.Column("started_at", sa.DateTime(timezone=True)), sa.Column("completed_at", sa.DateTime(timezone=True)),
|
||||
sa.ForeignKeyConstraint(["project_id"], ["projects.id"], ondelete="RESTRICT"),
|
||||
sa.ForeignKeyConstraint(["suite_revision_id"], ["evaluation_suite_revisions.id"], ondelete="RESTRICT"),
|
||||
sa.ForeignKeyConstraint(["capability_deployment_id"], ["capability_deployments.id"], ondelete="RESTRICT"),
|
||||
)
|
||||
for column in ("project_id", "suite_revision_id", "capability_deployment_id", "status", "environment_digest"):
|
||||
op.create_index(f"ix_evaluation_runs_{column}", "evaluation_runs", [column])
|
||||
op.create_table(
|
||||
"evaluation_case_results", _id(), _created(),
|
||||
sa.Column("run_id", sa.Uuid(), nullable=False), sa.Column("case_id", sa.Uuid(), nullable=False),
|
||||
sa.Column("ranked_results", sa.JSON(), nullable=False), sa.Column("relevant_results", sa.JSON(), nullable=False),
|
||||
sa.Column("first_relevant_rank", sa.Integer()), sa.Column("metrics", sa.JSON(), nullable=False),
|
||||
sa.Column("latency_ms", sa.Float(), nullable=False), sa.Column("error_code", sa.String(64)),
|
||||
sa.ForeignKeyConstraint(["run_id"], ["evaluation_runs.id"], ondelete="RESTRICT"),
|
||||
sa.ForeignKeyConstraint(["case_id"], ["evaluation_cases.id"], ondelete="RESTRICT"),
|
||||
sa.UniqueConstraint("run_id", "case_id", name="uq_evaluation_case_result"),
|
||||
)
|
||||
op.create_index("ix_evaluation_case_results_run_id", "evaluation_case_results", ["run_id"])
|
||||
op.create_index("ix_evaluation_case_results_case_id", "evaluation_case_results", ["case_id"])
|
||||
op.create_table(
|
||||
"evaluation_comparisons", _id(), _created(),
|
||||
sa.Column("project_id", sa.Uuid(), nullable=False), sa.Column("baseline_run_id", sa.Uuid(), nullable=False),
|
||||
sa.Column("candidate_run_id", sa.Uuid(), nullable=False), sa.Column("comparability", sa.String(32), nullable=False),
|
||||
sa.Column("comparability_evidence", sa.JSON(), nullable=False), sa.Column("metric_deltas", sa.JSON(), nullable=False),
|
||||
sa.Column("improved_cases", sa.Integer(), nullable=False), sa.Column("unchanged_cases", sa.Integer(), nullable=False),
|
||||
sa.Column("regressed_cases", sa.Integer(), nullable=False), sa.Column("critical_regressions", sa.Integer(), nullable=False),
|
||||
sa.Column("case_comparisons", sa.JSON(), nullable=False), sa.Column("promotion_eligibility", sa.String(32), nullable=False),
|
||||
sa.Column("eligibility_evidence", sa.JSON(), nullable=False),
|
||||
sa.ForeignKeyConstraint(["project_id"], ["projects.id"], ondelete="RESTRICT"),
|
||||
sa.ForeignKeyConstraint(["baseline_run_id"], ["evaluation_runs.id"], ondelete="RESTRICT"),
|
||||
sa.ForeignKeyConstraint(["candidate_run_id"], ["evaluation_runs.id"], ondelete="RESTRICT"),
|
||||
sa.UniqueConstraint("baseline_run_id", "candidate_run_id", name="uq_evaluation_comparison"),
|
||||
)
|
||||
for column in ("project_id", "baseline_run_id", "candidate_run_id"):
|
||||
op.create_index(f"ix_evaluation_comparisons_{column}", "evaluation_comparisons", [column])
|
||||
|
||||
|
||||
def downgrade() -> None:
|
||||
for table in (
|
||||
"evaluation_comparisons", "evaluation_case_results", "evaluation_runs", "evaluation_cases",
|
||||
"evaluation_suite_revisions", "evaluation_suites", "embedding_migrations", "project_evaluation_bindings",
|
||||
):
|
||||
op.drop_table(table)
|
||||
op.drop_column("service_clients", "workload_priority")
|
||||
@@ -0,0 +1,103 @@
|
||||
"""M7 candidate capability routes and lab approval provenance.
|
||||
|
||||
Revision ID: 20260825_0010
|
||||
Revises: 20260825_0009
|
||||
"""
|
||||
|
||||
from collections.abc import Sequence
|
||||
|
||||
import sqlalchemy as sa
|
||||
|
||||
from alembic import op
|
||||
|
||||
revision: str = "20260825_0010"
|
||||
down_revision: str | None = "20260825_0009"
|
||||
branch_labels: str | Sequence[str] | None = None
|
||||
depends_on: str | Sequence[str] | None = None
|
||||
|
||||
|
||||
def upgrade() -> None:
|
||||
op.alter_column(
|
||||
"capability_deployments",
|
||||
"production_approval_id",
|
||||
existing_type=sa.Uuid(),
|
||||
nullable=True,
|
||||
)
|
||||
op.add_column(
|
||||
"capability_deployments",
|
||||
sa.Column("execution_approval_id", sa.Uuid(), nullable=True),
|
||||
)
|
||||
op.create_index(
|
||||
"ix_capability_deployments_execution_approval_id",
|
||||
"capability_deployments",
|
||||
["execution_approval_id"],
|
||||
)
|
||||
op.create_foreign_key(
|
||||
"fk_capability_deployments_execution_approval_id",
|
||||
"capability_deployments",
|
||||
"execution_approvals",
|
||||
["execution_approval_id"],
|
||||
["id"],
|
||||
ondelete="RESTRICT",
|
||||
)
|
||||
op.create_table(
|
||||
"capability_experiment_routes",
|
||||
sa.Column("route_key", sa.String(length=128), nullable=False),
|
||||
sa.Column("capability_contract_id", sa.Uuid(), nullable=False),
|
||||
sa.Column("capability_deployment_id", sa.Uuid(), nullable=False),
|
||||
sa.Column("status", sa.String(length=32), nullable=False),
|
||||
sa.Column("purpose", sa.Text(), nullable=False),
|
||||
sa.Column("evidence", sa.JSON(), nullable=False),
|
||||
sa.Column("id", sa.Uuid(), nullable=False),
|
||||
sa.Column("created_at", sa.DateTime(timezone=True), server_default=sa.func.now(), nullable=False),
|
||||
sa.Column("updated_at", sa.DateTime(timezone=True), server_default=sa.func.now(), nullable=False),
|
||||
sa.ForeignKeyConstraint(
|
||||
["capability_contract_id"], ["capability_contracts.id"], ondelete="RESTRICT"
|
||||
),
|
||||
sa.ForeignKeyConstraint(
|
||||
["capability_deployment_id"], ["capability_deployments.id"], ondelete="RESTRICT"
|
||||
),
|
||||
sa.PrimaryKeyConstraint("id"),
|
||||
sa.UniqueConstraint("route_key"),
|
||||
)
|
||||
op.create_index(
|
||||
"ix_capability_experiment_routes_route_key",
|
||||
"capability_experiment_routes",
|
||||
["route_key"],
|
||||
unique=True,
|
||||
)
|
||||
op.create_index(
|
||||
"ix_capability_experiment_routes_capability_contract_id",
|
||||
"capability_experiment_routes",
|
||||
["capability_contract_id"],
|
||||
)
|
||||
op.create_index(
|
||||
"ix_capability_experiment_routes_capability_deployment_id",
|
||||
"capability_experiment_routes",
|
||||
["capability_deployment_id"],
|
||||
)
|
||||
op.create_index(
|
||||
"ix_capability_experiment_routes_status",
|
||||
"capability_experiment_routes",
|
||||
["status"],
|
||||
)
|
||||
|
||||
|
||||
def downgrade() -> None:
|
||||
op.drop_table("capability_experiment_routes")
|
||||
op.drop_constraint(
|
||||
"fk_capability_deployments_execution_approval_id",
|
||||
"capability_deployments",
|
||||
type_="foreignkey",
|
||||
)
|
||||
op.drop_index(
|
||||
"ix_capability_deployments_execution_approval_id",
|
||||
table_name="capability_deployments",
|
||||
)
|
||||
op.drop_column("capability_deployments", "execution_approval_id")
|
||||
op.alter_column(
|
||||
"capability_deployments",
|
||||
"production_approval_id",
|
||||
existing_type=sa.Uuid(),
|
||||
nullable=False,
|
||||
)
|
||||
@@ -0,0 +1,109 @@
|
||||
"""M7 first-class model comparisons and deterministic advisor evidence.
|
||||
|
||||
Revision ID: 20260825_0011
|
||||
Revises: 20260825_0010
|
||||
"""
|
||||
|
||||
from collections.abc import Sequence
|
||||
|
||||
import sqlalchemy as sa
|
||||
|
||||
from alembic import op
|
||||
|
||||
revision: str = "20260825_0011"
|
||||
down_revision: str | None = "20260825_0010"
|
||||
branch_labels: str | Sequence[str] | None = None
|
||||
depends_on: str | Sequence[str] | None = None
|
||||
|
||||
|
||||
def upgrade() -> None:
|
||||
op.create_table(
|
||||
"model_comparisons",
|
||||
sa.Column("project_id", sa.Uuid(), nullable=False),
|
||||
sa.Column("capability_contract_id", sa.Uuid(), nullable=False),
|
||||
sa.Column("suite_revision_id", sa.Uuid(), nullable=False),
|
||||
sa.Column("current_run_id", sa.Uuid(), nullable=False),
|
||||
sa.Column("title", sa.String(length=255), nullable=False),
|
||||
sa.Column("candidates", sa.JSON(), nullable=False),
|
||||
sa.Column("comparability", sa.String(length=32), nullable=False),
|
||||
sa.Column("evidence_fingerprint", sa.String(length=64), nullable=False),
|
||||
sa.Column("id", sa.Uuid(), nullable=False),
|
||||
sa.Column("created_at", sa.DateTime(timezone=True), server_default=sa.func.now(), nullable=False),
|
||||
sa.ForeignKeyConstraint(["project_id"], ["projects.id"], ondelete="RESTRICT"),
|
||||
sa.ForeignKeyConstraint(["capability_contract_id"], ["capability_contracts.id"], ondelete="RESTRICT"),
|
||||
sa.ForeignKeyConstraint(["suite_revision_id"], ["evaluation_suite_revisions.id"], ondelete="RESTRICT"),
|
||||
sa.ForeignKeyConstraint(["current_run_id"], ["evaluation_runs.id"], ondelete="RESTRICT"),
|
||||
sa.PrimaryKeyConstraint("id"),
|
||||
sa.UniqueConstraint("evidence_fingerprint"),
|
||||
)
|
||||
op.create_index("ix_model_comparisons_project_id", "model_comparisons", ["project_id"])
|
||||
op.create_index("ix_model_comparisons_capability_contract_id", "model_comparisons", ["capability_contract_id"])
|
||||
op.create_index("ix_model_comparisons_suite_revision_id", "model_comparisons", ["suite_revision_id"])
|
||||
|
||||
op.create_table(
|
||||
"advisor_policies",
|
||||
sa.Column("key", sa.String(length=128), nullable=False),
|
||||
sa.Column("required_evidence_level", sa.String(length=8), nullable=False),
|
||||
sa.Column("critical_regression_hard_block", sa.Boolean(), nullable=False),
|
||||
sa.Column("maximum_latency_p95_regression_ratio", sa.Float(), nullable=False),
|
||||
sa.Column("minimum_metric_deltas", sa.JSON(), nullable=False),
|
||||
sa.Column("require_verified_supply_chain", sa.Boolean(), nullable=False),
|
||||
sa.Column("require_runtime_fit", sa.Boolean(), nullable=False),
|
||||
sa.Column("rationale", sa.Text(), nullable=False),
|
||||
sa.Column("version", sa.Integer(), nullable=False),
|
||||
sa.Column("id", sa.Uuid(), nullable=False),
|
||||
sa.Column("created_at", sa.DateTime(timezone=True), server_default=sa.func.now(), nullable=False),
|
||||
sa.Column("updated_at", sa.DateTime(timezone=True), server_default=sa.func.now(), nullable=False),
|
||||
sa.PrimaryKeyConstraint("id"),
|
||||
sa.UniqueConstraint("key"),
|
||||
)
|
||||
|
||||
op.create_table(
|
||||
"advisor_recommendations",
|
||||
sa.Column("comparison_id", sa.Uuid(), nullable=False),
|
||||
sa.Column("project_id", sa.Uuid(), nullable=False),
|
||||
sa.Column("capability_contract_id", sa.Uuid(), nullable=False),
|
||||
sa.Column("policy_id", sa.Uuid(), nullable=False),
|
||||
sa.Column("candidate_key", sa.String(length=128), nullable=False),
|
||||
sa.Column("current_deployment_id", sa.Uuid(), nullable=True),
|
||||
sa.Column("candidate_deployment_id", sa.Uuid(), nullable=True),
|
||||
sa.Column("current_embedding_space", sa.String(length=255), nullable=False),
|
||||
sa.Column("candidate_embedding_space", sa.String(length=255), nullable=True),
|
||||
sa.Column("verdict", sa.String(length=64), nullable=False),
|
||||
sa.Column("confidence", sa.String(length=16), nullable=False),
|
||||
sa.Column("evidence_level", sa.String(length=8), nullable=False),
|
||||
sa.Column("quality_deltas", sa.JSON(), nullable=False),
|
||||
sa.Column("latency_deltas", sa.JSON(), nullable=False),
|
||||
sa.Column("resource_deltas", sa.JSON(), nullable=False),
|
||||
sa.Column("migration_impact", sa.JSON(), nullable=False),
|
||||
sa.Column("security_state", sa.JSON(), nullable=False),
|
||||
sa.Column("key_improvements", sa.JSON(), nullable=False),
|
||||
sa.Column("blockers", sa.JSON(), nullable=False),
|
||||
sa.Column("policy_snapshot", sa.JSON(), nullable=False),
|
||||
sa.Column("evidence_fingerprint", sa.String(length=64), nullable=False),
|
||||
sa.Column("status", sa.String(length=32), nullable=False),
|
||||
sa.Column("generated_at", sa.DateTime(timezone=True), nullable=False),
|
||||
sa.Column("dismissed_at", sa.DateTime(timezone=True), nullable=True),
|
||||
sa.Column("dismissed_by", sa.String(length=255), nullable=True),
|
||||
sa.Column("dismissal_reason", sa.Text(), nullable=True),
|
||||
sa.Column("id", sa.Uuid(), nullable=False),
|
||||
sa.ForeignKeyConstraint(["comparison_id"], ["model_comparisons.id"], ondelete="RESTRICT"),
|
||||
sa.ForeignKeyConstraint(["project_id"], ["projects.id"], ondelete="RESTRICT"),
|
||||
sa.ForeignKeyConstraint(["capability_contract_id"], ["capability_contracts.id"], ondelete="RESTRICT"),
|
||||
sa.ForeignKeyConstraint(["policy_id"], ["advisor_policies.id"], ondelete="RESTRICT"),
|
||||
sa.ForeignKeyConstraint(["current_deployment_id"], ["capability_deployments.id"], ondelete="RESTRICT"),
|
||||
sa.ForeignKeyConstraint(["candidate_deployment_id"], ["capability_deployments.id"], ondelete="RESTRICT"),
|
||||
sa.PrimaryKeyConstraint("id"),
|
||||
sa.UniqueConstraint("evidence_fingerprint"),
|
||||
)
|
||||
op.create_index("ix_advisor_recommendations_comparison_id", "advisor_recommendations", ["comparison_id"])
|
||||
op.create_index("ix_advisor_recommendations_project_id", "advisor_recommendations", ["project_id"])
|
||||
op.create_index("ix_advisor_recommendations_capability_contract_id", "advisor_recommendations", ["capability_contract_id"])
|
||||
op.create_index("ix_advisor_recommendations_verdict", "advisor_recommendations", ["verdict"])
|
||||
op.create_index("ix_advisor_recommendations_status", "advisor_recommendations", ["status"])
|
||||
|
||||
|
||||
def downgrade() -> None:
|
||||
op.drop_table("advisor_recommendations")
|
||||
op.drop_table("advisor_policies")
|
||||
op.drop_table("model_comparisons")
|
||||
@@ -0,0 +1,276 @@
|
||||
"""M8 frozen reranking pools and retrieval pipeline evidence.
|
||||
|
||||
Revision ID: 20260825_0012
|
||||
Revises: 20260825_0011
|
||||
"""
|
||||
|
||||
from collections.abc import Sequence
|
||||
|
||||
import sqlalchemy as sa
|
||||
|
||||
from alembic import op
|
||||
|
||||
revision: str = "20260825_0012"
|
||||
down_revision: str | None = "20260825_0011"
|
||||
branch_labels: str | Sequence[str] | None = None
|
||||
depends_on: str | Sequence[str] | None = None
|
||||
|
||||
|
||||
def upgrade() -> None:
|
||||
op.alter_column("capability_deployments", "embedding_space_id", nullable=True)
|
||||
|
||||
op.create_table(
|
||||
"retrieval_candidate_pools",
|
||||
sa.Column("project_id", sa.Uuid(), nullable=False),
|
||||
sa.Column("suite_revision_id", sa.Uuid(), nullable=False),
|
||||
sa.Column("evaluation_case_id", sa.Uuid(), nullable=False),
|
||||
sa.Column("source_embedding_space", sa.String(length=255), nullable=False),
|
||||
sa.Column("source_index_ref", sa.Text(), nullable=False),
|
||||
sa.Column("corpus_revision", sa.String(length=128), nullable=False),
|
||||
sa.Column("retrieval_config_digest", sa.String(length=64), nullable=False),
|
||||
sa.Column("candidate_count", sa.Integer(), nullable=False),
|
||||
sa.Column("ordered_candidates", sa.JSON(), nullable=False),
|
||||
sa.Column("fingerprint", sa.String(length=64), nullable=False),
|
||||
sa.Column("id", sa.Uuid(), nullable=False),
|
||||
sa.Column(
|
||||
"created_at", sa.DateTime(timezone=True), server_default=sa.func.now(), nullable=False
|
||||
),
|
||||
sa.Column(
|
||||
"immutable_at", sa.DateTime(timezone=True), server_default=sa.func.now(), nullable=False
|
||||
),
|
||||
sa.CheckConstraint(
|
||||
"candidate_count > 0 AND candidate_count <= 40", name="ck_candidate_pool_count"
|
||||
),
|
||||
sa.ForeignKeyConstraint(["project_id"], ["projects.id"], ondelete="RESTRICT"),
|
||||
sa.ForeignKeyConstraint(
|
||||
["suite_revision_id"], ["evaluation_suite_revisions.id"], ondelete="RESTRICT"
|
||||
),
|
||||
sa.ForeignKeyConstraint(
|
||||
["evaluation_case_id"], ["evaluation_cases.id"], ondelete="RESTRICT"
|
||||
),
|
||||
sa.PrimaryKeyConstraint("id"),
|
||||
sa.UniqueConstraint(
|
||||
"evaluation_case_id",
|
||||
"source_index_ref",
|
||||
"fingerprint",
|
||||
name="uq_candidate_pool_case_source_fingerprint",
|
||||
),
|
||||
sa.UniqueConstraint("fingerprint"),
|
||||
)
|
||||
op.create_index("ix_candidate_pools_project_id", "retrieval_candidate_pools", ["project_id"])
|
||||
op.create_index(
|
||||
"ix_candidate_pools_suite_revision_id", "retrieval_candidate_pools", ["suite_revision_id"]
|
||||
)
|
||||
op.create_index(
|
||||
"ix_candidate_pools_evaluation_case_id", "retrieval_candidate_pools", ["evaluation_case_id"]
|
||||
)
|
||||
|
||||
op.create_table(
|
||||
"retrieval_pipeline_identities",
|
||||
sa.Column("project_id", sa.Uuid(), nullable=False),
|
||||
sa.Column("embedding_space_ref", sa.String(length=255), nullable=False),
|
||||
sa.Column("sparse_config_digest", sa.String(length=64), nullable=False),
|
||||
sa.Column("fusion_config_digest", sa.String(length=64), nullable=False),
|
||||
sa.Column("reranker_deployment_id", sa.Uuid(), nullable=True),
|
||||
sa.Column("reranker_config_digest", sa.String(length=64), nullable=True),
|
||||
sa.Column("candidate_k", sa.Integer(), nullable=False),
|
||||
sa.Column("output_k", sa.Integer(), nullable=False),
|
||||
sa.Column("identity_digest", sa.String(length=64), nullable=False),
|
||||
sa.Column("configuration", sa.JSON(), nullable=False),
|
||||
sa.Column("migration_class", sa.String(length=32), nullable=False),
|
||||
sa.Column("id", sa.Uuid(), nullable=False),
|
||||
sa.Column(
|
||||
"created_at", sa.DateTime(timezone=True), server_default=sa.func.now(), nullable=False
|
||||
),
|
||||
sa.Column(
|
||||
"immutable_at", sa.DateTime(timezone=True), server_default=sa.func.now(), nullable=False
|
||||
),
|
||||
sa.CheckConstraint("candidate_k > 0 AND candidate_k <= 40", name="ck_pipeline_candidate_k"),
|
||||
sa.CheckConstraint("output_k > 0 AND output_k <= candidate_k", name="ck_pipeline_output_k"),
|
||||
sa.ForeignKeyConstraint(["project_id"], ["projects.id"], ondelete="RESTRICT"),
|
||||
sa.ForeignKeyConstraint(
|
||||
["reranker_deployment_id"], ["capability_deployments.id"], ondelete="RESTRICT"
|
||||
),
|
||||
sa.PrimaryKeyConstraint("id"),
|
||||
sa.UniqueConstraint("identity_digest"),
|
||||
)
|
||||
op.create_index(
|
||||
"ix_pipeline_identities_project_id", "retrieval_pipeline_identities", ["project_id"]
|
||||
)
|
||||
op.create_index(
|
||||
"ix_pipeline_identities_reranker_deployment_id",
|
||||
"retrieval_pipeline_identities",
|
||||
["reranker_deployment_id"],
|
||||
)
|
||||
|
||||
op.create_table(
|
||||
"reranking_evaluation_runs",
|
||||
sa.Column("project_id", sa.Uuid(), nullable=False),
|
||||
sa.Column("suite_revision_id", sa.Uuid(), nullable=False),
|
||||
sa.Column("pipeline_identity_id", sa.Uuid(), nullable=False),
|
||||
sa.Column("control_pipeline_identity_id", sa.Uuid(), nullable=False),
|
||||
sa.Column("reranker_deployment_id", sa.Uuid(), nullable=True),
|
||||
sa.Column("status", sa.String(length=32), nullable=False),
|
||||
sa.Column("corpus_revision", sa.String(length=128), nullable=False),
|
||||
sa.Column("candidate_pool_set_fingerprint", sa.String(length=64), nullable=False),
|
||||
sa.Column("environment_fingerprint", sa.JSON(), nullable=False),
|
||||
sa.Column("environment_digest", sa.String(length=64), nullable=False),
|
||||
sa.Column("expected_cases", sa.Integer(), nullable=False),
|
||||
sa.Column("completed_cases", sa.Integer(), nullable=False),
|
||||
sa.Column("error_count", sa.Integer(), nullable=False),
|
||||
sa.Column("aggregate_metrics", sa.JSON(), nullable=False),
|
||||
sa.Column("latency_metrics", sa.JSON(), nullable=False),
|
||||
sa.Column("started_at", sa.DateTime(timezone=True), nullable=True),
|
||||
sa.Column("completed_at", sa.DateTime(timezone=True), nullable=True),
|
||||
sa.Column("id", sa.Uuid(), nullable=False),
|
||||
sa.Column(
|
||||
"created_at", sa.DateTime(timezone=True), server_default=sa.func.now(), nullable=False
|
||||
),
|
||||
sa.ForeignKeyConstraint(["project_id"], ["projects.id"], ondelete="RESTRICT"),
|
||||
sa.ForeignKeyConstraint(
|
||||
["suite_revision_id"], ["evaluation_suite_revisions.id"], ondelete="RESTRICT"
|
||||
),
|
||||
sa.ForeignKeyConstraint(
|
||||
["pipeline_identity_id"], ["retrieval_pipeline_identities.id"], ondelete="RESTRICT"
|
||||
),
|
||||
sa.ForeignKeyConstraint(
|
||||
["control_pipeline_identity_id"],
|
||||
["retrieval_pipeline_identities.id"],
|
||||
ondelete="RESTRICT",
|
||||
),
|
||||
sa.ForeignKeyConstraint(
|
||||
["reranker_deployment_id"], ["capability_deployments.id"], ondelete="RESTRICT"
|
||||
),
|
||||
sa.PrimaryKeyConstraint("id"),
|
||||
)
|
||||
op.create_index("ix_reranking_runs_project_id", "reranking_evaluation_runs", ["project_id"])
|
||||
op.create_index(
|
||||
"ix_reranking_runs_suite_revision_id", "reranking_evaluation_runs", ["suite_revision_id"]
|
||||
)
|
||||
op.create_index(
|
||||
"ix_reranking_runs_pipeline_identity_id",
|
||||
"reranking_evaluation_runs",
|
||||
["pipeline_identity_id"],
|
||||
)
|
||||
op.create_index("ix_reranking_runs_status", "reranking_evaluation_runs", ["status"])
|
||||
|
||||
op.create_table(
|
||||
"reranking_case_results",
|
||||
sa.Column("run_id", sa.Uuid(), nullable=False),
|
||||
sa.Column("case_id", sa.Uuid(), nullable=False),
|
||||
sa.Column("candidate_pool_id", sa.Uuid(), nullable=False),
|
||||
sa.Column("ranked_results", sa.JSON(), nullable=False),
|
||||
sa.Column("relevant_results", sa.JSON(), nullable=False),
|
||||
sa.Column("first_relevant_rank", sa.Integer(), nullable=True),
|
||||
sa.Column("metrics", sa.JSON(), nullable=False),
|
||||
sa.Column("retrieval_latency_ms", sa.Float(), nullable=False),
|
||||
sa.Column("rerank_latency_ms", sa.Float(), nullable=False),
|
||||
sa.Column("total_latency_ms", sa.Float(), nullable=False),
|
||||
sa.Column("error_code", sa.String(length=64), nullable=True),
|
||||
sa.Column("id", sa.Uuid(), nullable=False),
|
||||
sa.Column(
|
||||
"created_at", sa.DateTime(timezone=True), server_default=sa.func.now(), nullable=False
|
||||
),
|
||||
sa.ForeignKeyConstraint(["run_id"], ["reranking_evaluation_runs.id"], ondelete="RESTRICT"),
|
||||
sa.ForeignKeyConstraint(["case_id"], ["evaluation_cases.id"], ondelete="RESTRICT"),
|
||||
sa.ForeignKeyConstraint(
|
||||
["candidate_pool_id"], ["retrieval_candidate_pools.id"], ondelete="RESTRICT"
|
||||
),
|
||||
sa.PrimaryKeyConstraint("id"),
|
||||
sa.UniqueConstraint("run_id", "case_id", name="uq_reranking_case_result"),
|
||||
)
|
||||
op.create_index("ix_reranking_case_results_run_id", "reranking_case_results", ["run_id"])
|
||||
op.create_index("ix_reranking_case_results_case_id", "reranking_case_results", ["case_id"])
|
||||
op.create_index(
|
||||
"ix_reranking_case_results_pool_id", "reranking_case_results", ["candidate_pool_id"]
|
||||
)
|
||||
|
||||
op.create_table(
|
||||
"discovery_candidate_assessments",
|
||||
sa.Column("model_id", sa.Uuid(), nullable=True),
|
||||
sa.Column("upstream_snapshot_id", sa.Uuid(), nullable=False),
|
||||
sa.Column("candidate_key", sa.String(length=128), nullable=False),
|
||||
sa.Column("repository_id", sa.String(length=255), nullable=False),
|
||||
sa.Column("resolved_commit_sha", sa.String(length=64), nullable=False),
|
||||
sa.Column("artifact_evidence", sa.JSON(), nullable=False),
|
||||
sa.Column("security_state", sa.JSON(), nullable=False),
|
||||
sa.Column("license_state", sa.JSON(), nullable=False),
|
||||
sa.Column("gpu_fit", sa.JSON(), nullable=False),
|
||||
sa.Column("status", sa.String(length=32), nullable=False),
|
||||
sa.Column("rationale", sa.Text(), nullable=False),
|
||||
sa.Column("evidence_fingerprint", sa.String(length=64), nullable=False),
|
||||
sa.Column("id", sa.Uuid(), nullable=False),
|
||||
sa.Column(
|
||||
"created_at", sa.DateTime(timezone=True), server_default=sa.func.now(), nullable=False
|
||||
),
|
||||
sa.Column(
|
||||
"immutable_at", sa.DateTime(timezone=True), server_default=sa.func.now(), nullable=False
|
||||
),
|
||||
sa.ForeignKeyConstraint(["model_id"], ["models.id"], ondelete="RESTRICT"),
|
||||
sa.ForeignKeyConstraint(
|
||||
["upstream_snapshot_id"], ["upstream_snapshots.id"], ondelete="RESTRICT"
|
||||
),
|
||||
sa.PrimaryKeyConstraint("id"),
|
||||
sa.UniqueConstraint("evidence_fingerprint"),
|
||||
)
|
||||
op.create_index(
|
||||
"ix_discovery_assessments_model_id", "discovery_candidate_assessments", ["model_id"]
|
||||
)
|
||||
op.create_index(
|
||||
"ix_discovery_assessments_snapshot_id",
|
||||
"discovery_candidate_assessments",
|
||||
["upstream_snapshot_id"],
|
||||
)
|
||||
op.create_index(
|
||||
"ix_discovery_assessments_status", "discovery_candidate_assessments", ["status"]
|
||||
)
|
||||
|
||||
op.add_column(
|
||||
"advisor_recommendations",
|
||||
sa.Column(
|
||||
"target_kind",
|
||||
sa.String(length=32),
|
||||
nullable=False,
|
||||
server_default="embedding_deployment",
|
||||
),
|
||||
)
|
||||
op.add_column(
|
||||
"advisor_recommendations",
|
||||
sa.Column("current_pipeline_identity_id", sa.Uuid(), nullable=True),
|
||||
)
|
||||
op.add_column(
|
||||
"advisor_recommendations",
|
||||
sa.Column("candidate_pipeline_identity_id", sa.Uuid(), nullable=True),
|
||||
)
|
||||
op.create_foreign_key(
|
||||
"fk_advisor_current_pipeline",
|
||||
"advisor_recommendations",
|
||||
"retrieval_pipeline_identities",
|
||||
["current_pipeline_identity_id"],
|
||||
["id"],
|
||||
ondelete="RESTRICT",
|
||||
)
|
||||
op.create_foreign_key(
|
||||
"fk_advisor_candidate_pipeline",
|
||||
"advisor_recommendations",
|
||||
"retrieval_pipeline_identities",
|
||||
["candidate_pipeline_identity_id"],
|
||||
["id"],
|
||||
ondelete="RESTRICT",
|
||||
)
|
||||
|
||||
|
||||
def downgrade() -> None:
|
||||
op.drop_constraint(
|
||||
"fk_advisor_candidate_pipeline", "advisor_recommendations", type_="foreignkey"
|
||||
)
|
||||
op.drop_constraint("fk_advisor_current_pipeline", "advisor_recommendations", type_="foreignkey")
|
||||
op.drop_column("advisor_recommendations", "candidate_pipeline_identity_id")
|
||||
op.drop_column("advisor_recommendations", "current_pipeline_identity_id")
|
||||
op.drop_column("advisor_recommendations", "target_kind")
|
||||
op.drop_table("discovery_candidate_assessments")
|
||||
op.drop_table("reranking_case_results")
|
||||
op.drop_table("reranking_evaluation_runs")
|
||||
op.drop_table("retrieval_pipeline_identities")
|
||||
op.drop_table("retrieval_candidate_pools")
|
||||
op.alter_column("capability_deployments", "embedding_space_id", nullable=False)
|
||||
@@ -0,0 +1,74 @@
|
||||
"""M9 multimodal capability evaluation foundation.
|
||||
|
||||
Revision ID: 20260826_0013
|
||||
Revises: 20260825_0012
|
||||
"""
|
||||
|
||||
from collections.abc import Sequence
|
||||
|
||||
import sqlalchemy as sa
|
||||
from alembic import op
|
||||
|
||||
revision: str = "20260826_0013"
|
||||
down_revision: str | None = "20260825_0012"
|
||||
branch_labels: str | Sequence[str] | None = None
|
||||
depends_on: str | Sequence[str] | None = None
|
||||
|
||||
|
||||
def upgrade() -> None:
|
||||
op.create_table(
|
||||
"capability_evaluation_suites",
|
||||
sa.Column("id", sa.Uuid(), nullable=False),
|
||||
sa.Column("capability_contract_id", sa.Uuid(), nullable=False),
|
||||
sa.Column("key", sa.String(length=128), nullable=False),
|
||||
sa.Column("evaluation_type", sa.String(length=32), nullable=False),
|
||||
sa.Column("revision", sa.String(length=128), nullable=False),
|
||||
sa.Column("dataset_revision", sa.String(length=128), nullable=False),
|
||||
sa.Column("definition_digest", sa.String(length=64), nullable=False),
|
||||
sa.Column("metric_definitions", sa.JSON(), nullable=False),
|
||||
sa.Column("case_definitions", sa.JSON(), nullable=False),
|
||||
sa.Column("thresholds", sa.JSON(), nullable=False),
|
||||
sa.Column("created_at", sa.DateTime(timezone=True), server_default=sa.func.now(), nullable=False),
|
||||
sa.Column("immutable_at", sa.DateTime(timezone=True), server_default=sa.func.now(), nullable=False),
|
||||
sa.ForeignKeyConstraint(["capability_contract_id"], ["capability_contracts.id"], ondelete="RESTRICT"),
|
||||
sa.PrimaryKeyConstraint("id"),
|
||||
sa.UniqueConstraint("capability_contract_id", "key", "revision", name="uq_capability_eval_suite"),
|
||||
sa.UniqueConstraint("definition_digest", name="uq_capability_eval_definition_digest"),
|
||||
)
|
||||
op.create_index("ix_capability_evaluation_suites_capability_contract_id", "capability_evaluation_suites", ["capability_contract_id"])
|
||||
op.create_index("ix_capability_evaluation_suites_evaluation_type", "capability_evaluation_suites", ["evaluation_type"])
|
||||
op.create_table(
|
||||
"capability_evaluation_runs",
|
||||
sa.Column("id", sa.Uuid(), nullable=False),
|
||||
sa.Column("suite_id", sa.Uuid(), nullable=False),
|
||||
sa.Column("capability_deployment_id", sa.Uuid(), nullable=False),
|
||||
sa.Column("status", sa.String(length=32), nullable=False),
|
||||
sa.Column("metric_values", sa.JSON(), nullable=False),
|
||||
sa.Column("case_results", sa.JSON(), nullable=False),
|
||||
sa.Column("resource_metrics", sa.JSON(), nullable=False),
|
||||
sa.Column("environment_fingerprint", sa.String(length=64), nullable=False),
|
||||
sa.Column("evidence_digest", sa.String(length=64), nullable=False),
|
||||
sa.Column("evidence", sa.JSON(), nullable=False),
|
||||
sa.Column("started_at", sa.DateTime(timezone=True), nullable=False),
|
||||
sa.Column("completed_at", sa.DateTime(timezone=True), nullable=False),
|
||||
sa.Column("created_at", sa.DateTime(timezone=True), server_default=sa.func.now(), nullable=False),
|
||||
sa.ForeignKeyConstraint(["suite_id"], ["capability_evaluation_suites.id"], ondelete="RESTRICT"),
|
||||
sa.ForeignKeyConstraint(["capability_deployment_id"], ["capability_deployments.id"], ondelete="RESTRICT"),
|
||||
sa.PrimaryKeyConstraint("id"),
|
||||
sa.UniqueConstraint("evidence_digest", name="uq_capability_eval_evidence"),
|
||||
)
|
||||
op.create_index("ix_capability_evaluation_runs_suite_id", "capability_evaluation_runs", ["suite_id"])
|
||||
op.create_index("ix_capability_evaluation_runs_capability_deployment_id", "capability_evaluation_runs", ["capability_deployment_id"])
|
||||
op.create_index("ix_capability_evaluation_runs_status", "capability_evaluation_runs", ["status"])
|
||||
op.create_index("ix_capability_evaluation_runs_environment_fingerprint", "capability_evaluation_runs", ["environment_fingerprint"])
|
||||
|
||||
|
||||
def downgrade() -> None:
|
||||
op.drop_index("ix_capability_evaluation_runs_environment_fingerprint", table_name="capability_evaluation_runs")
|
||||
op.drop_index("ix_capability_evaluation_runs_status", table_name="capability_evaluation_runs")
|
||||
op.drop_index("ix_capability_evaluation_runs_capability_deployment_id", table_name="capability_evaluation_runs")
|
||||
op.drop_index("ix_capability_evaluation_runs_suite_id", table_name="capability_evaluation_runs")
|
||||
op.drop_table("capability_evaluation_runs")
|
||||
op.drop_index("ix_capability_evaluation_suites_evaluation_type", table_name="capability_evaluation_suites")
|
||||
op.drop_index("ix_capability_evaluation_suites_capability_contract_id", table_name="capability_evaluation_suites")
|
||||
op.drop_table("capability_evaluation_suites")
|
||||
@@ -0,0 +1,232 @@
|
||||
"""M10 advanced residency and GPU scheduling.
|
||||
|
||||
Revision ID: 20260826_0014
|
||||
Revises: 20260826_0013
|
||||
"""
|
||||
|
||||
import uuid
|
||||
from collections.abc import Sequence
|
||||
|
||||
import sqlalchemy as sa
|
||||
|
||||
from alembic import op
|
||||
|
||||
revision: str = "20260826_0014"
|
||||
down_revision: str | None = "20260826_0013"
|
||||
branch_labels: str | Sequence[str] | None = None
|
||||
depends_on: str | Sequence[str] | None = None
|
||||
|
||||
|
||||
def upgrade() -> None:
|
||||
op.add_column(
|
||||
"residency_allocations",
|
||||
sa.Column("generation", sa.Integer(), server_default="1", nullable=False),
|
||||
)
|
||||
op.add_column(
|
||||
"residency_allocations", sa.Column("transition_reason", sa.String(length=64), nullable=True)
|
||||
)
|
||||
op.add_column(
|
||||
"serving_gpu_leases",
|
||||
sa.Column(
|
||||
"lease_type", sa.String(length=32), server_default="request_execution", nullable=False
|
||||
),
|
||||
)
|
||||
op.add_column(
|
||||
"serving_gpu_leases",
|
||||
sa.Column("materialized_vram_bytes", sa.BigInteger(), server_default="0", nullable=False),
|
||||
)
|
||||
op.add_column("serving_gpu_leases", sa.Column("serving_job_id", sa.Uuid(), nullable=True))
|
||||
op.add_column(
|
||||
"serving_gpu_leases",
|
||||
sa.Column("generation", sa.Integer(), server_default="1", nullable=False),
|
||||
)
|
||||
op.create_foreign_key(
|
||||
"fk_serving_gpu_leases_job",
|
||||
"serving_gpu_leases",
|
||||
"serving_jobs",
|
||||
["serving_job_id"],
|
||||
["id"],
|
||||
ondelete="SET NULL",
|
||||
)
|
||||
op.create_index(
|
||||
"ix_serving_gpu_leases_serving_job_id", "serving_gpu_leases", ["serving_job_id"]
|
||||
)
|
||||
op.create_table(
|
||||
"scheduler_policy_revisions",
|
||||
sa.Column("id", sa.Uuid(), nullable=False),
|
||||
sa.Column("revision", sa.String(length=64), nullable=False),
|
||||
sa.Column("configuration", sa.JSON(), nullable=False),
|
||||
sa.Column("active", sa.Boolean(), nullable=False),
|
||||
sa.Column(
|
||||
"created_at", sa.DateTime(timezone=True), server_default=sa.func.now(), nullable=False
|
||||
),
|
||||
sa.PrimaryKeyConstraint("id"),
|
||||
sa.UniqueConstraint("revision"),
|
||||
)
|
||||
op.create_index(
|
||||
"ix_scheduler_policy_revisions_active", "scheduler_policy_revisions", ["active"]
|
||||
)
|
||||
policy_table = sa.table(
|
||||
"scheduler_policy_revisions",
|
||||
sa.column("id", sa.Uuid()),
|
||||
sa.column("revision", sa.String()),
|
||||
sa.column("configuration", sa.JSON()),
|
||||
sa.column("active", sa.Boolean()),
|
||||
)
|
||||
op.bulk_insert(
|
||||
policy_table,
|
||||
[
|
||||
{
|
||||
"id": uuid.UUID("8e4d6a85-8e50-4f78-a182-000000000010"),
|
||||
"revision": "m10-v1",
|
||||
"configuration": {
|
||||
"reserve_minimum_bytes": 1073741824,
|
||||
"reserve_percentage": 0.05,
|
||||
"runtime_margin_bytes": 268435456,
|
||||
"deployment_margin_minimum_bytes": 134217728,
|
||||
"deployment_margin_percentage": 0.1,
|
||||
"request_execution_floor_bytes": 67108864,
|
||||
"pressure_stable_seconds": 30,
|
||||
"eviction_cooldown_seconds": 60,
|
||||
"global_queue_limit": 128,
|
||||
"placement_history_limit": 500,
|
||||
"lab_paused": False,
|
||||
},
|
||||
"active": True,
|
||||
}
|
||||
],
|
||||
)
|
||||
op.create_table(
|
||||
"scheduler_accelerator_states",
|
||||
sa.Column("id", sa.Uuid(), nullable=False),
|
||||
sa.Column("accelerator_id", sa.Uuid(), nullable=False),
|
||||
sa.Column("pressure_state", sa.String(length=16), nullable=False),
|
||||
sa.Column("recovery_candidate", sa.String(length=16), nullable=True),
|
||||
sa.Column("recovery_candidate_since", sa.DateTime(timezone=True), nullable=True),
|
||||
sa.Column(
|
||||
"pressure_changed_at",
|
||||
sa.DateTime(timezone=True),
|
||||
server_default=sa.func.now(),
|
||||
nullable=False,
|
||||
),
|
||||
sa.Column(
|
||||
"last_observed_at",
|
||||
sa.DateTime(timezone=True),
|
||||
server_default=sa.func.now(),
|
||||
nullable=False,
|
||||
),
|
||||
sa.ForeignKeyConstraint(["accelerator_id"], ["accelerators.id"], ondelete="CASCADE"),
|
||||
sa.PrimaryKeyConstraint("id"),
|
||||
sa.UniqueConstraint("accelerator_id"),
|
||||
)
|
||||
op.create_index(
|
||||
"ix_scheduler_accelerator_states_accelerator_id",
|
||||
"scheduler_accelerator_states",
|
||||
["accelerator_id"],
|
||||
unique=True,
|
||||
)
|
||||
op.create_table(
|
||||
"placement_plans",
|
||||
sa.Column("id", sa.Uuid(), nullable=False),
|
||||
sa.Column("capability_deployment_id", sa.Uuid(), nullable=False),
|
||||
sa.Column("request_id", sa.Uuid(), nullable=True),
|
||||
sa.Column("policy_revision", sa.String(length=64), nullable=False),
|
||||
sa.Column("verdict", sa.String(length=32), nullable=False),
|
||||
sa.Column("reason_codes", sa.JSON(), nullable=False),
|
||||
sa.Column("evidence", sa.JSON(), nullable=False),
|
||||
sa.Column("evidence_fingerprint", sa.String(length=64), nullable=False),
|
||||
sa.Column("dry_run", sa.Boolean(), nullable=False),
|
||||
sa.Column(
|
||||
"created_at", sa.DateTime(timezone=True), server_default=sa.func.now(), nullable=False
|
||||
),
|
||||
sa.ForeignKeyConstraint(
|
||||
["capability_deployment_id"], ["capability_deployments.id"], ondelete="RESTRICT"
|
||||
),
|
||||
sa.PrimaryKeyConstraint("id"),
|
||||
)
|
||||
for column in (
|
||||
"capability_deployment_id",
|
||||
"request_id",
|
||||
"verdict",
|
||||
"evidence_fingerprint",
|
||||
"created_at",
|
||||
):
|
||||
op.create_index(f"ix_placement_plans_{column}", "placement_plans", [column])
|
||||
op.create_table(
|
||||
"co_residency_evidence",
|
||||
sa.Column("id", sa.Uuid(), nullable=False),
|
||||
sa.Column("left_deployment_id", sa.Uuid(), nullable=False),
|
||||
sa.Column("right_deployment_id", sa.Uuid(), nullable=False),
|
||||
sa.Column("status", sa.String(length=32), nullable=False),
|
||||
sa.Column("expected_combined_bytes", sa.BigInteger(), nullable=False),
|
||||
sa.Column("measured_combined_bytes", sa.BigInteger(), nullable=True),
|
||||
sa.Column("evidence", sa.JSON(), nullable=False),
|
||||
sa.Column("measured_at", sa.DateTime(timezone=True), nullable=True),
|
||||
sa.Column(
|
||||
"created_at", sa.DateTime(timezone=True), server_default=sa.func.now(), nullable=False
|
||||
),
|
||||
sa.ForeignKeyConstraint(
|
||||
["left_deployment_id"], ["capability_deployments.id"], ondelete="RESTRICT"
|
||||
),
|
||||
sa.ForeignKeyConstraint(
|
||||
["right_deployment_id"], ["capability_deployments.id"], ondelete="RESTRICT"
|
||||
),
|
||||
sa.PrimaryKeyConstraint("id"),
|
||||
sa.UniqueConstraint(
|
||||
"left_deployment_id", "right_deployment_id", name="uq_co_residency_pair"
|
||||
),
|
||||
)
|
||||
for column in ("left_deployment_id", "right_deployment_id", "status"):
|
||||
op.create_index(f"ix_co_residency_evidence_{column}", "co_residency_evidence", [column])
|
||||
op.create_table(
|
||||
"scheduler_evictions",
|
||||
sa.Column("id", sa.Uuid(), nullable=False),
|
||||
sa.Column("capability_deployment_id", sa.Uuid(), nullable=False),
|
||||
sa.Column("requested_deployment_id", sa.Uuid(), nullable=True),
|
||||
sa.Column("reason_code", sa.String(length=64), nullable=False),
|
||||
sa.Column("expected_reclaimed_bytes", sa.BigInteger(), nullable=False),
|
||||
sa.Column("actual_reclaimed_bytes", sa.BigInteger(), nullable=True),
|
||||
sa.Column("state", sa.String(length=32), nullable=False),
|
||||
sa.Column(
|
||||
"created_at", sa.DateTime(timezone=True), server_default=sa.func.now(), nullable=False
|
||||
),
|
||||
sa.ForeignKeyConstraint(
|
||||
["capability_deployment_id"], ["capability_deployments.id"], ondelete="RESTRICT"
|
||||
),
|
||||
sa.ForeignKeyConstraint(
|
||||
["requested_deployment_id"], ["capability_deployments.id"], ondelete="SET NULL"
|
||||
),
|
||||
sa.PrimaryKeyConstraint("id"),
|
||||
)
|
||||
for column in ("capability_deployment_id", "requested_deployment_id", "reason_code"):
|
||||
op.create_index(f"ix_scheduler_evictions_{column}", "scheduler_evictions", [column])
|
||||
|
||||
|
||||
def downgrade() -> None:
|
||||
op.drop_index(
|
||||
"ix_scheduler_accelerator_states_accelerator_id", table_name="scheduler_accelerator_states"
|
||||
)
|
||||
op.drop_table("scheduler_accelerator_states")
|
||||
for column in ("capability_deployment_id", "requested_deployment_id", "reason_code"):
|
||||
op.drop_index(f"ix_scheduler_evictions_{column}", table_name="scheduler_evictions")
|
||||
op.drop_table("scheduler_evictions")
|
||||
for column in ("left_deployment_id", "right_deployment_id", "status"):
|
||||
op.drop_index(f"ix_co_residency_evidence_{column}", table_name="co_residency_evidence")
|
||||
op.drop_table("co_residency_evidence")
|
||||
for column in (
|
||||
"capability_deployment_id",
|
||||
"request_id",
|
||||
"verdict",
|
||||
"evidence_fingerprint",
|
||||
"created_at",
|
||||
):
|
||||
op.drop_index(f"ix_placement_plans_{column}", table_name="placement_plans")
|
||||
op.drop_table("placement_plans")
|
||||
op.drop_index("ix_scheduler_policy_revisions_active", table_name="scheduler_policy_revisions")
|
||||
op.drop_table("scheduler_policy_revisions")
|
||||
op.drop_index("ix_serving_gpu_leases_serving_job_id", table_name="serving_gpu_leases")
|
||||
op.drop_constraint("fk_serving_gpu_leases_job", "serving_gpu_leases", type_="foreignkey")
|
||||
for column in ("generation", "serving_job_id", "materialized_vram_bytes", "lease_type"):
|
||||
op.drop_column("serving_gpu_leases", column)
|
||||
op.drop_column("residency_allocations", "transition_reason")
|
||||
op.drop_column("residency_allocations", "generation")
|
||||
@@ -0,0 +1,54 @@
|
||||
"""Use 64-bit scheduler and worker generations.
|
||||
|
||||
Revision ID: 20260826_0015
|
||||
Revises: 20260826_0014
|
||||
"""
|
||||
|
||||
from collections.abc import Sequence
|
||||
|
||||
import sqlalchemy as sa
|
||||
|
||||
from alembic import op
|
||||
|
||||
revision: str = "20260826_0015"
|
||||
down_revision: str | None = "20260826_0014"
|
||||
branch_labels: str | Sequence[str] | None = None
|
||||
depends_on: str | Sequence[str] | None = None
|
||||
|
||||
|
||||
def upgrade() -> None:
|
||||
op.alter_column(
|
||||
"residency_allocations",
|
||||
"generation",
|
||||
existing_type=sa.Integer(),
|
||||
type_=sa.BigInteger(),
|
||||
existing_nullable=False,
|
||||
existing_server_default="1",
|
||||
)
|
||||
op.alter_column(
|
||||
"serving_gpu_leases",
|
||||
"generation",
|
||||
existing_type=sa.Integer(),
|
||||
type_=sa.BigInteger(),
|
||||
existing_nullable=False,
|
||||
existing_server_default="1",
|
||||
)
|
||||
|
||||
|
||||
def downgrade() -> None:
|
||||
op.alter_column(
|
||||
"serving_gpu_leases",
|
||||
"generation",
|
||||
existing_type=sa.BigInteger(),
|
||||
type_=sa.Integer(),
|
||||
existing_nullable=False,
|
||||
existing_server_default="1",
|
||||
)
|
||||
op.alter_column(
|
||||
"residency_allocations",
|
||||
"generation",
|
||||
existing_type=sa.BigInteger(),
|
||||
type_=sa.Integer(),
|
||||
existing_nullable=False,
|
||||
existing_server_default="1",
|
||||
)
|
||||
@@ -0,0 +1,65 @@
|
||||
"""Add operational project integrations and project-fit evidence.
|
||||
|
||||
Revision ID: 20260826_0016
|
||||
Revises: 20260826_0015
|
||||
"""
|
||||
|
||||
from collections.abc import Sequence
|
||||
|
||||
import sqlalchemy as sa
|
||||
|
||||
from alembic import op
|
||||
|
||||
revision: str = "20260826_0016"
|
||||
down_revision: str | None = "20260826_0015"
|
||||
branch_labels: str | Sequence[str] | None = None
|
||||
depends_on: str | Sequence[str] | None = None
|
||||
|
||||
|
||||
def upgrade() -> None:
|
||||
op.add_column("service_clients", sa.Column("project_binding_id", sa.Uuid(), nullable=True))
|
||||
op.add_column("service_clients", sa.Column("integration_environment", sa.String(32), nullable=True))
|
||||
op.add_column("service_clients", sa.Column("purpose", sa.Text(), nullable=True))
|
||||
op.create_foreign_key(
|
||||
"fk_service_clients_project_binding",
|
||||
"service_clients",
|
||||
"project_bindings",
|
||||
["project_binding_id"],
|
||||
["id"],
|
||||
ondelete="RESTRICT",
|
||||
)
|
||||
op.create_index("ix_service_clients_project_binding_id", "service_clients", ["project_binding_id"])
|
||||
op.create_table(
|
||||
"project_fit_evidence",
|
||||
sa.Column("id", sa.Uuid(), nullable=False),
|
||||
sa.Column("project_binding_id", sa.Uuid(), nullable=False),
|
||||
sa.Column("capability_deployment_id", sa.Uuid(), nullable=True),
|
||||
sa.Column("environment", sa.String(32), nullable=False),
|
||||
sa.Column("recommendation", sa.String(32), nullable=False),
|
||||
sa.Column("case_count", sa.Integer(), nullable=False),
|
||||
sa.Column("metric_values", sa.JSON(), nullable=False),
|
||||
sa.Column("critical_errors", sa.Integer(), nullable=False),
|
||||
sa.Column("blockers", sa.JSON(), nullable=False),
|
||||
sa.Column("evidence_digest", sa.String(64), nullable=False),
|
||||
sa.Column("evidence", sa.JSON(), nullable=False),
|
||||
sa.Column("created_at", sa.DateTime(timezone=True), server_default=sa.func.now(), nullable=False),
|
||||
sa.ForeignKeyConstraint(["project_binding_id"], ["project_bindings.id"], ondelete="RESTRICT"),
|
||||
sa.ForeignKeyConstraint(["capability_deployment_id"], ["capability_deployments.id"], ondelete="SET NULL"),
|
||||
sa.PrimaryKeyConstraint("id"),
|
||||
sa.UniqueConstraint("evidence_digest", name="uq_project_fit_evidence_digest"),
|
||||
)
|
||||
op.create_index("ix_project_fit_evidence_project_binding_id", "project_fit_evidence", ["project_binding_id"])
|
||||
op.create_index("ix_project_fit_evidence_capability_deployment_id", "project_fit_evidence", ["capability_deployment_id"])
|
||||
op.create_index("ix_project_fit_evidence_recommendation", "project_fit_evidence", ["recommendation"])
|
||||
|
||||
|
||||
def downgrade() -> None:
|
||||
op.drop_index("ix_project_fit_evidence_recommendation", table_name="project_fit_evidence")
|
||||
op.drop_index("ix_project_fit_evidence_capability_deployment_id", table_name="project_fit_evidence")
|
||||
op.drop_index("ix_project_fit_evidence_project_binding_id", table_name="project_fit_evidence")
|
||||
op.drop_table("project_fit_evidence")
|
||||
op.drop_index("ix_service_clients_project_binding_id", table_name="service_clients")
|
||||
op.drop_constraint("fk_service_clients_project_binding", "service_clients", type_="foreignkey")
|
||||
op.drop_column("service_clients", "purpose")
|
||||
op.drop_column("service_clients", "integration_environment")
|
||||
op.drop_column("service_clients", "project_binding_id")
|
||||
@@ -0,0 +1,82 @@
|
||||
"""Classify project-fit evidence and defer production validation explicitly.
|
||||
|
||||
Revision ID: 20260826_0017
|
||||
Revises: 20260826_0016
|
||||
"""
|
||||
|
||||
from collections.abc import Sequence
|
||||
|
||||
import sqlalchemy as sa
|
||||
|
||||
from alembic import op
|
||||
|
||||
revision: str = "20260826_0017"
|
||||
down_revision: str | None = "20260826_0016"
|
||||
branch_labels: str | Sequence[str] | None = None
|
||||
depends_on: str | Sequence[str] | None = None
|
||||
|
||||
|
||||
def upgrade() -> None:
|
||||
op.add_column(
|
||||
"project_fit_evidence",
|
||||
sa.Column(
|
||||
"evidence_class",
|
||||
sa.String(32),
|
||||
server_default="CATALOG_REFERENCE",
|
||||
nullable=False,
|
||||
),
|
||||
)
|
||||
op.add_column(
|
||||
"project_fit_evidence",
|
||||
sa.Column(
|
||||
"engineering_integration",
|
||||
sa.String(32),
|
||||
server_default="PASS",
|
||||
nullable=False,
|
||||
),
|
||||
)
|
||||
op.add_column(
|
||||
"project_fit_evidence",
|
||||
sa.Column(
|
||||
"production_validation",
|
||||
sa.String(48),
|
||||
server_default="DEFERRED_EXTERNAL_VALIDATION",
|
||||
nullable=False,
|
||||
),
|
||||
)
|
||||
op.add_column(
|
||||
"project_fit_evidence",
|
||||
sa.Column("production_action", sa.String(32), server_default="NONE", nullable=False),
|
||||
)
|
||||
op.create_index(
|
||||
"ix_project_fit_evidence_evidence_class",
|
||||
"project_fit_evidence",
|
||||
["evidence_class"],
|
||||
)
|
||||
op.create_index(
|
||||
"ix_project_fit_evidence_production_validation",
|
||||
"project_fit_evidence",
|
||||
["production_validation"],
|
||||
)
|
||||
for column in (
|
||||
"evidence_class",
|
||||
"engineering_integration",
|
||||
"production_validation",
|
||||
"production_action",
|
||||
):
|
||||
op.alter_column("project_fit_evidence", column, server_default=None)
|
||||
|
||||
|
||||
def downgrade() -> None:
|
||||
op.drop_index(
|
||||
"ix_project_fit_evidence_production_validation",
|
||||
table_name="project_fit_evidence",
|
||||
)
|
||||
op.drop_index(
|
||||
"ix_project_fit_evidence_evidence_class",
|
||||
table_name="project_fit_evidence",
|
||||
)
|
||||
op.drop_column("project_fit_evidence", "production_action")
|
||||
op.drop_column("project_fit_evidence", "production_validation")
|
||||
op.drop_column("project_fit_evidence", "engineering_integration")
|
||||
op.drop_column("project_fit_evidence", "evidence_class")
|
||||
@@ -0,0 +1,431 @@
|
||||
"""Add evidence-bound lifecycle, rollback, retention and cleanup records.
|
||||
|
||||
Revision ID: 20260826_0018
|
||||
Revises: 20260826_0017
|
||||
"""
|
||||
|
||||
from collections.abc import Sequence
|
||||
|
||||
import sqlalchemy as sa
|
||||
|
||||
from alembic import op
|
||||
|
||||
revision: str = "20260826_0018"
|
||||
down_revision: str | None = "20260826_0017"
|
||||
branch_labels: str | Sequence[str] | None = None
|
||||
depends_on: str | Sequence[str] | None = None
|
||||
|
||||
|
||||
def id_column() -> sa.Column[object]:
|
||||
return sa.Column("id", sa.Uuid(), primary_key=True, nullable=False)
|
||||
|
||||
|
||||
def created_at() -> sa.Column[object]:
|
||||
return sa.Column("created_at", sa.DateTime(timezone=True), server_default=sa.func.now())
|
||||
|
||||
|
||||
def upgrade() -> None:
|
||||
op.create_table(
|
||||
"lifecycle_subjects",
|
||||
id_column(),
|
||||
sa.Column("target_type", sa.String(48), nullable=False),
|
||||
sa.Column("target_ref", sa.String(255), nullable=False),
|
||||
sa.Column("environment", sa.String(32), nullable=False),
|
||||
sa.Column("state", sa.String(48), nullable=False),
|
||||
sa.Column("version", sa.Integer(), nullable=False, server_default="1"),
|
||||
sa.Column("superseded_by_ref", sa.String(255)),
|
||||
created_at(),
|
||||
sa.Column("updated_at", sa.DateTime(timezone=True), server_default=sa.func.now()),
|
||||
sa.CheckConstraint("version >= 1", name="ck_lifecycle_subject_version"),
|
||||
sa.UniqueConstraint(
|
||||
"target_type", "target_ref", "environment", name="uq_lifecycle_subject_target"
|
||||
),
|
||||
)
|
||||
op.create_index("ix_lifecycle_subjects_target_type", "lifecycle_subjects", ["target_type"])
|
||||
op.create_index("ix_lifecycle_subjects_target_ref", "lifecycle_subjects", ["target_ref"])
|
||||
op.create_index("ix_lifecycle_subjects_environment", "lifecycle_subjects", ["environment"])
|
||||
op.create_index("ix_lifecycle_subjects_state", "lifecycle_subjects", ["state"])
|
||||
|
||||
op.create_table(
|
||||
"lifecycle_policy_revisions",
|
||||
id_column(),
|
||||
sa.Column("key", sa.String(128), nullable=False),
|
||||
sa.Column("revision", sa.Integer(), nullable=False),
|
||||
sa.Column("scope", sa.String(48), nullable=False),
|
||||
sa.Column("requirements", sa.JSON(), nullable=False),
|
||||
sa.Column("fingerprint", sa.String(64), nullable=False),
|
||||
sa.Column("active", sa.Boolean(), nullable=False, server_default=sa.true()),
|
||||
sa.Column("created_by", sa.String(255), nullable=False),
|
||||
created_at(),
|
||||
sa.Column("immutable_at", sa.DateTime(timezone=True), server_default=sa.func.now()),
|
||||
sa.UniqueConstraint("key", "revision", name="uq_lifecycle_policy_revision"),
|
||||
sa.UniqueConstraint("fingerprint", name="uq_lifecycle_policy_fingerprint"),
|
||||
)
|
||||
op.create_index("ix_lifecycle_policy_revisions_key", "lifecycle_policy_revisions", ["key"])
|
||||
op.create_index("ix_lifecycle_policy_revisions_scope", "lifecycle_policy_revisions", ["scope"])
|
||||
op.create_index(
|
||||
"ix_lifecycle_policy_revisions_active", "lifecycle_policy_revisions", ["active"]
|
||||
)
|
||||
|
||||
op.create_table(
|
||||
"lifecycle_approval_requests",
|
||||
id_column(),
|
||||
sa.Column(
|
||||
"policy_revision_id",
|
||||
sa.Uuid(),
|
||||
sa.ForeignKey("lifecycle_policy_revisions.id", ondelete="RESTRICT"),
|
||||
nullable=False,
|
||||
),
|
||||
sa.Column(
|
||||
"subject_id",
|
||||
sa.Uuid(),
|
||||
sa.ForeignKey("lifecycle_subjects.id", ondelete="RESTRICT"),
|
||||
),
|
||||
sa.Column("target_type", sa.String(48), nullable=False),
|
||||
sa.Column("target_ref", sa.String(255), nullable=False),
|
||||
sa.Column("environment", sa.String(32), nullable=False),
|
||||
sa.Column("requested_transition", sa.String(48), nullable=False),
|
||||
sa.Column("evidence_snapshot", sa.JSON(), nullable=False),
|
||||
sa.Column("evidence_fingerprint", sa.String(64), nullable=False),
|
||||
sa.Column("status", sa.String(32), nullable=False),
|
||||
sa.Column("blockers", sa.JSON(), nullable=False),
|
||||
sa.Column("warnings", sa.JSON(), nullable=False),
|
||||
sa.Column("requested_by", sa.String(255), nullable=False),
|
||||
sa.Column("approved_by", sa.String(255)),
|
||||
sa.Column("reason", sa.Text(), nullable=False),
|
||||
sa.Column("expires_at", sa.DateTime(timezone=True)),
|
||||
sa.Column("stale_at", sa.DateTime(timezone=True)),
|
||||
sa.Column("decided_at", sa.DateTime(timezone=True)),
|
||||
sa.Column("version", sa.Integer(), nullable=False, server_default="1"),
|
||||
created_at(),
|
||||
sa.CheckConstraint("version >= 1", name="ck_lifecycle_approval_version"),
|
||||
)
|
||||
for column in (
|
||||
"policy_revision_id",
|
||||
"subject_id",
|
||||
"target_type",
|
||||
"target_ref",
|
||||
"environment",
|
||||
"evidence_fingerprint",
|
||||
"status",
|
||||
"expires_at",
|
||||
"created_at",
|
||||
):
|
||||
op.create_index(
|
||||
f"ix_lifecycle_approval_requests_{column}", "lifecycle_approval_requests", [column]
|
||||
)
|
||||
|
||||
op.create_table(
|
||||
"lifecycle_approval_evidence",
|
||||
sa.Column(
|
||||
"approval_request_id",
|
||||
sa.Uuid(),
|
||||
sa.ForeignKey("lifecycle_approval_requests.id", ondelete="RESTRICT"),
|
||||
primary_key=True,
|
||||
),
|
||||
sa.Column("evidence_type", sa.String(64), primary_key=True),
|
||||
sa.Column("evidence_ref", sa.String(255), primary_key=True),
|
||||
sa.Column("evidence_digest", sa.String(64), nullable=False),
|
||||
)
|
||||
op.create_index(
|
||||
"ix_lifecycle_approval_evidence_evidence_ref",
|
||||
"lifecycle_approval_evidence",
|
||||
["evidence_ref"],
|
||||
)
|
||||
|
||||
op.create_table(
|
||||
"lifecycle_promotion_plans",
|
||||
id_column(),
|
||||
sa.Column(
|
||||
"approval_request_id",
|
||||
sa.Uuid(),
|
||||
sa.ForeignKey("lifecycle_approval_requests.id", ondelete="RESTRICT"),
|
||||
nullable=False,
|
||||
),
|
||||
sa.Column(
|
||||
"subject_id",
|
||||
sa.Uuid(),
|
||||
sa.ForeignKey("lifecycle_subjects.id", ondelete="RESTRICT"),
|
||||
nullable=False,
|
||||
),
|
||||
sa.Column("current_state", sa.String(48), nullable=False),
|
||||
sa.Column("desired_state", sa.String(48), nullable=False),
|
||||
sa.Column("migration_class", sa.String(32), nullable=False),
|
||||
sa.Column(
|
||||
"candidate_deployment_id",
|
||||
sa.Uuid(),
|
||||
sa.ForeignKey("capability_deployments.id", ondelete="RESTRICT"),
|
||||
),
|
||||
sa.Column("rollback_target_ref", sa.String(255), nullable=False),
|
||||
sa.Column("project_consumers", sa.JSON(), nullable=False),
|
||||
sa.Column("affected_identities", sa.JSON(), nullable=False),
|
||||
sa.Column("impact_analysis", sa.JSON(), nullable=False),
|
||||
sa.Column(
|
||||
"migration_id",
|
||||
sa.Uuid(),
|
||||
sa.ForeignKey("embedding_migrations.id", ondelete="RESTRICT"),
|
||||
),
|
||||
sa.Column("canary_strategy", sa.JSON(), nullable=False),
|
||||
sa.Column("drain_strategy", sa.JSON(), nullable=False),
|
||||
sa.Column("health_gates", sa.JSON(), nullable=False),
|
||||
sa.Column("automatic_abort_conditions", sa.JSON(), nullable=False),
|
||||
sa.Column("plan_fingerprint", sa.String(64), nullable=False),
|
||||
sa.Column("status", sa.String(32), nullable=False, server_default="DRAFT"),
|
||||
sa.Column("version", sa.Integer(), nullable=False, server_default="1"),
|
||||
sa.Column("created_by", sa.String(255), nullable=False),
|
||||
sa.Column("approved_by", sa.String(255)),
|
||||
sa.Column("immutable_at", sa.DateTime(timezone=True)),
|
||||
created_at(),
|
||||
sa.CheckConstraint("version >= 1", name="ck_lifecycle_plan_version"),
|
||||
sa.UniqueConstraint("plan_fingerprint", name="uq_lifecycle_plan_fingerprint"),
|
||||
)
|
||||
for column in (
|
||||
"approval_request_id",
|
||||
"subject_id",
|
||||
"migration_class",
|
||||
"candidate_deployment_id",
|
||||
"migration_id",
|
||||
"status",
|
||||
"created_at",
|
||||
):
|
||||
op.create_index(
|
||||
f"ix_lifecycle_promotion_plans_{column}", "lifecycle_promotion_plans", [column]
|
||||
)
|
||||
|
||||
op.create_table(
|
||||
"lifecycle_rollback_snapshots",
|
||||
id_column(),
|
||||
sa.Column(
|
||||
"promotion_plan_id",
|
||||
sa.Uuid(),
|
||||
sa.ForeignKey("lifecycle_promotion_plans.id", ondelete="RESTRICT"),
|
||||
nullable=False,
|
||||
),
|
||||
sa.Column("snapshot", sa.JSON(), nullable=False),
|
||||
sa.Column("snapshot_digest", sa.String(64), nullable=False),
|
||||
created_at(),
|
||||
sa.Column("immutable_at", sa.DateTime(timezone=True), server_default=sa.func.now()),
|
||||
sa.UniqueConstraint("promotion_plan_id", name="uq_lifecycle_rollback_plan"),
|
||||
sa.UniqueConstraint("snapshot_digest", name="uq_lifecycle_rollback_digest"),
|
||||
)
|
||||
op.create_index(
|
||||
"ix_lifecycle_rollback_snapshots_promotion_plan_id",
|
||||
"lifecycle_rollback_snapshots",
|
||||
["promotion_plan_id"],
|
||||
)
|
||||
|
||||
op.create_table(
|
||||
"lifecycle_operations",
|
||||
id_column(),
|
||||
sa.Column(
|
||||
"promotion_plan_id",
|
||||
sa.Uuid(),
|
||||
sa.ForeignKey("lifecycle_promotion_plans.id", ondelete="RESTRICT"),
|
||||
nullable=False,
|
||||
),
|
||||
sa.Column("stage", sa.String(32), nullable=False),
|
||||
sa.Column("idempotency_key", sa.String(128), nullable=False),
|
||||
sa.Column("expected_subject_version", sa.Integer(), nullable=False),
|
||||
sa.Column("requester", sa.String(255), nullable=False),
|
||||
sa.Column("approver", sa.String(255), nullable=False),
|
||||
sa.Column("executor", sa.String(255), nullable=False),
|
||||
sa.Column("failure_code", sa.String(64)),
|
||||
sa.Column("failure_details", sa.JSON(), nullable=False),
|
||||
sa.Column("rollback_duration_ms", sa.Float()),
|
||||
sa.Column("version", sa.Integer(), nullable=False, server_default="1"),
|
||||
sa.Column("started_at", sa.DateTime(timezone=True), server_default=sa.func.now()),
|
||||
sa.Column("finished_at", sa.DateTime(timezone=True)),
|
||||
sa.CheckConstraint("version >= 1", name="ck_lifecycle_operation_version"),
|
||||
sa.UniqueConstraint("idempotency_key", name="uq_lifecycle_operation_idempotency"),
|
||||
)
|
||||
op.create_index(
|
||||
"ix_lifecycle_operations_promotion_plan_id", "lifecycle_operations", ["promotion_plan_id"]
|
||||
)
|
||||
op.create_index("ix_lifecycle_operations_stage", "lifecycle_operations", ["stage"])
|
||||
|
||||
op.create_table(
|
||||
"lifecycle_canary_runs",
|
||||
id_column(),
|
||||
sa.Column(
|
||||
"operation_id",
|
||||
sa.Uuid(),
|
||||
sa.ForeignKey("lifecycle_operations.id", ondelete="RESTRICT"),
|
||||
nullable=False,
|
||||
),
|
||||
sa.Column("mode", sa.String(48), nullable=False),
|
||||
sa.Column("traffic_percent", sa.Integer(), nullable=False, server_default="0"),
|
||||
sa.Column("eligible_clients", sa.JSON(), nullable=False),
|
||||
sa.Column("eligible_projects", sa.JSON(), nullable=False),
|
||||
sa.Column("target_request_count", sa.Integer(), nullable=False),
|
||||
sa.Column("thresholds", sa.JSON(), nullable=False),
|
||||
sa.Column("status", sa.String(48), nullable=False),
|
||||
sa.Column("request_count", sa.Integer(), nullable=False, server_default="0"),
|
||||
sa.Column("error_count", sa.Integer(), nullable=False, server_default="0"),
|
||||
sa.Column("latency_p95_ms", sa.Float()),
|
||||
sa.Column("abort_trigger", sa.String(64)),
|
||||
sa.Column("result", sa.JSON(), nullable=False),
|
||||
sa.Column("started_at", sa.DateTime(timezone=True), server_default=sa.func.now()),
|
||||
sa.Column("finished_at", sa.DateTime(timezone=True)),
|
||||
sa.UniqueConstraint("operation_id", name="uq_lifecycle_canary_operation"),
|
||||
)
|
||||
op.create_index(
|
||||
"ix_lifecycle_canary_runs_operation_id", "lifecycle_canary_runs", ["operation_id"]
|
||||
)
|
||||
op.create_index("ix_lifecycle_canary_runs_status", "lifecycle_canary_runs", ["status"])
|
||||
|
||||
op.create_table(
|
||||
"retention_policy_revisions",
|
||||
id_column(),
|
||||
sa.Column("key", sa.String(128), nullable=False),
|
||||
sa.Column("revision", sa.Integer(), nullable=False),
|
||||
sa.Column("minimum_rollback_days", sa.Integer(), nullable=False),
|
||||
sa.Column("requirements", sa.JSON(), nullable=False),
|
||||
sa.Column("fingerprint", sa.String(64), nullable=False),
|
||||
sa.Column("active", sa.Boolean(), nullable=False, server_default=sa.true()),
|
||||
sa.Column("created_by", sa.String(255), nullable=False),
|
||||
created_at(),
|
||||
sa.Column("immutable_at", sa.DateTime(timezone=True), server_default=sa.func.now()),
|
||||
sa.UniqueConstraint("key", "revision", name="uq_retention_policy_revision"),
|
||||
sa.UniqueConstraint("fingerprint", name="uq_retention_policy_fingerprint"),
|
||||
)
|
||||
op.create_index("ix_retention_policy_revisions_key", "retention_policy_revisions", ["key"])
|
||||
op.create_index(
|
||||
"ix_retention_policy_revisions_active", "retention_policy_revisions", ["active"]
|
||||
)
|
||||
|
||||
op.create_table(
|
||||
"lifecycle_retention_records",
|
||||
id_column(),
|
||||
sa.Column(
|
||||
"policy_revision_id",
|
||||
sa.Uuid(),
|
||||
sa.ForeignKey("retention_policy_revisions.id", ondelete="RESTRICT"),
|
||||
nullable=False,
|
||||
),
|
||||
sa.Column("target_type", sa.String(48), nullable=False),
|
||||
sa.Column("target_ref", sa.String(255), nullable=False),
|
||||
sa.Column("state", sa.String(32), nullable=False),
|
||||
sa.Column("retained_until", sa.DateTime(timezone=True)),
|
||||
sa.Column("legal_hold", sa.Boolean(), nullable=False, server_default=sa.false()),
|
||||
sa.Column("reason", sa.Text(), nullable=False),
|
||||
created_at(),
|
||||
sa.UniqueConstraint("target_type", "target_ref", name="uq_lifecycle_retention_target"),
|
||||
)
|
||||
for column in ("policy_revision_id", "target_type", "target_ref", "state", "retained_until"):
|
||||
op.create_index(
|
||||
f"ix_lifecycle_retention_records_{column}", "lifecycle_retention_records", [column]
|
||||
)
|
||||
|
||||
op.create_table(
|
||||
"lifecycle_cleanup_plans",
|
||||
id_column(),
|
||||
sa.Column("target_type", sa.String(48), nullable=False),
|
||||
sa.Column("target_ref", sa.String(255), nullable=False),
|
||||
sa.Column("action", sa.String(48), nullable=False),
|
||||
sa.Column("dependencies", sa.JSON(), nullable=False),
|
||||
sa.Column("dependency_digest", sa.String(64), nullable=False),
|
||||
sa.Column("reclaimable_bytes", sa.BigInteger(), nullable=False),
|
||||
sa.Column("retention_state", sa.String(32), nullable=False),
|
||||
sa.Column("blockers", sa.JSON(), nullable=False),
|
||||
sa.Column("status", sa.String(32), nullable=False),
|
||||
sa.Column("created_by", sa.String(255), nullable=False),
|
||||
created_at(),
|
||||
sa.Column("executed_at", sa.DateTime(timezone=True)),
|
||||
)
|
||||
for column in ("target_type", "target_ref", "status", "created_at"):
|
||||
op.create_index(f"ix_lifecycle_cleanup_plans_{column}", "lifecycle_cleanup_plans", [column])
|
||||
|
||||
op.create_table(
|
||||
"artifact_location_removal_records",
|
||||
id_column(),
|
||||
sa.Column(
|
||||
"artifact_location_id",
|
||||
sa.Uuid(),
|
||||
sa.ForeignKey("artifact_locations.id", ondelete="RESTRICT"),
|
||||
nullable=False,
|
||||
),
|
||||
sa.Column(
|
||||
"cleanup_plan_id",
|
||||
sa.Uuid(),
|
||||
sa.ForeignKey("lifecycle_cleanup_plans.id", ondelete="RESTRICT"),
|
||||
nullable=False,
|
||||
),
|
||||
sa.Column("prior_status", sa.String(32), nullable=False),
|
||||
sa.Column("prior_observed_sha256", sa.String(64)),
|
||||
sa.Column("prior_size_bytes", sa.BigInteger()),
|
||||
sa.Column("removed_by", sa.String(255), nullable=False),
|
||||
sa.Column("removed_at", sa.DateTime(timezone=True), server_default=sa.func.now()),
|
||||
)
|
||||
op.create_index(
|
||||
"ix_artifact_location_removal_records_artifact_location_id",
|
||||
"artifact_location_removal_records",
|
||||
["artifact_location_id"],
|
||||
)
|
||||
op.create_index(
|
||||
"ix_artifact_location_removal_records_cleanup_plan_id",
|
||||
"artifact_location_removal_records",
|
||||
["cleanup_plan_id"],
|
||||
)
|
||||
|
||||
op.create_table(
|
||||
"lifecycle_events",
|
||||
id_column(),
|
||||
sa.Column("event_type", sa.String(64), nullable=False),
|
||||
sa.Column("object_type", sa.String(48), nullable=False),
|
||||
sa.Column("object_ref", sa.String(255), nullable=False),
|
||||
sa.Column("from_state", sa.String(48)),
|
||||
sa.Column("to_state", sa.String(48)),
|
||||
sa.Column("actor", sa.String(255), nullable=False),
|
||||
sa.Column("actor_role", sa.String(32), nullable=False),
|
||||
sa.Column(
|
||||
"policy_revision_id",
|
||||
sa.Uuid(),
|
||||
sa.ForeignKey("lifecycle_policy_revisions.id", ondelete="RESTRICT"),
|
||||
),
|
||||
sa.Column("evidence_ids", sa.JSON(), nullable=False),
|
||||
sa.Column("reason", sa.Text(), nullable=False),
|
||||
sa.Column("change_id", sa.String(64), nullable=False),
|
||||
sa.Column("details", sa.JSON(), nullable=False),
|
||||
sa.Column("occurred_at", sa.DateTime(timezone=True), server_default=sa.func.now()),
|
||||
)
|
||||
for column in (
|
||||
"event_type",
|
||||
"object_type",
|
||||
"object_ref",
|
||||
"policy_revision_id",
|
||||
"change_id",
|
||||
"occurred_at",
|
||||
):
|
||||
op.create_index(f"ix_lifecycle_events_{column}", "lifecycle_events", [column])
|
||||
|
||||
op.create_index(
|
||||
"uq_capability_deployment_one_production_stable",
|
||||
"capability_deployments",
|
||||
["capability_contract_id"],
|
||||
unique=True,
|
||||
postgresql_where=sa.text("production = true AND status = 'stable'"),
|
||||
sqlite_where=sa.text("production = 1 AND status = 'stable'"),
|
||||
)
|
||||
|
||||
|
||||
def downgrade() -> None:
|
||||
op.drop_index(
|
||||
"uq_capability_deployment_one_production_stable",
|
||||
table_name="capability_deployments",
|
||||
)
|
||||
for table in (
|
||||
"lifecycle_events",
|
||||
"artifact_location_removal_records",
|
||||
"lifecycle_cleanup_plans",
|
||||
"lifecycle_retention_records",
|
||||
"retention_policy_revisions",
|
||||
"lifecycle_canary_runs",
|
||||
"lifecycle_operations",
|
||||
"lifecycle_rollback_snapshots",
|
||||
"lifecycle_promotion_plans",
|
||||
"lifecycle_approval_evidence",
|
||||
"lifecycle_approval_requests",
|
||||
"lifecycle_policy_revisions",
|
||||
"lifecycle_subjects",
|
||||
):
|
||||
op.drop_table(table)
|
||||
@@ -0,0 +1,415 @@
|
||||
"""Add typed resumable migration-engine records.
|
||||
|
||||
Revision ID: 20260826_0019
|
||||
Revises: 20260826_0018
|
||||
"""
|
||||
|
||||
from collections.abc import Sequence
|
||||
|
||||
import sqlalchemy as sa
|
||||
|
||||
from alembic import op
|
||||
|
||||
revision: str = "20260826_0019"
|
||||
down_revision: str | None = "20260826_0018"
|
||||
branch_labels: str | Sequence[str] | None = None
|
||||
depends_on: str | Sequence[str] | None = None
|
||||
|
||||
|
||||
def _id() -> sa.Column[object]:
|
||||
return sa.Column("id", sa.Uuid(), primary_key=True, nullable=False)
|
||||
|
||||
|
||||
def _created() -> sa.Column[object]:
|
||||
return sa.Column("created_at", sa.DateTime(timezone=True), server_default=sa.func.now())
|
||||
|
||||
|
||||
def upgrade() -> None:
|
||||
op.create_table(
|
||||
"migration_validation_policy_revisions",
|
||||
_id(),
|
||||
sa.Column("key", sa.String(128), nullable=False),
|
||||
sa.Column("revision", sa.Integer(), nullable=False),
|
||||
sa.Column("required_completeness", sa.Float(), nullable=False),
|
||||
sa.Column("allowed_failures", sa.Integer(), nullable=False),
|
||||
sa.Column("required_evaluation", sa.Boolean(), nullable=False),
|
||||
sa.Column("critical_regressions_allowed", sa.Integer(), nullable=False),
|
||||
sa.Column("maximum_latency_regression_ratio", sa.Float()),
|
||||
sa.Column("require_project_fit", sa.Boolean(), nullable=False),
|
||||
sa.Column("require_external_validation", sa.Boolean(), nullable=False),
|
||||
sa.Column("require_security_approved", sa.Boolean(), nullable=False),
|
||||
sa.Column("allow_isolated_lab_cutover", sa.Boolean(), nullable=False),
|
||||
sa.Column("fingerprint", sa.String(64), nullable=False),
|
||||
sa.Column("active", sa.Boolean(), nullable=False, server_default=sa.true()),
|
||||
sa.Column("created_by", sa.String(255), nullable=False),
|
||||
_created(),
|
||||
sa.Column("immutable_at", sa.DateTime(timezone=True), server_default=sa.func.now()),
|
||||
sa.UniqueConstraint("key", "revision", name="uq_migration_validation_policy_revision"),
|
||||
sa.UniqueConstraint("fingerprint", name="uq_migration_validation_policy_fingerprint"),
|
||||
)
|
||||
op.create_index(
|
||||
"ix_migration_validation_policy_revisions_key",
|
||||
"migration_validation_policy_revisions",
|
||||
["key"],
|
||||
)
|
||||
op.create_index(
|
||||
"ix_migration_validation_policy_revisions_active",
|
||||
"migration_validation_policy_revisions",
|
||||
["active"],
|
||||
)
|
||||
|
||||
op.create_table(
|
||||
"migration_plans",
|
||||
_id(),
|
||||
sa.Column(
|
||||
"project_id",
|
||||
sa.Uuid(),
|
||||
sa.ForeignKey("projects.id", ondelete="RESTRICT"),
|
||||
nullable=False,
|
||||
),
|
||||
sa.Column(
|
||||
"project_binding_id",
|
||||
sa.Uuid(),
|
||||
sa.ForeignKey("project_bindings.id", ondelete="RESTRICT"),
|
||||
nullable=False,
|
||||
),
|
||||
sa.Column(
|
||||
"capability_contract_id",
|
||||
sa.Uuid(),
|
||||
sa.ForeignKey("capability_contracts.id", ondelete="RESTRICT"),
|
||||
nullable=False,
|
||||
),
|
||||
sa.Column("migration_class", sa.String(32), nullable=False),
|
||||
sa.Column("environment", sa.String(32), nullable=False),
|
||||
sa.Column("adapter", sa.JSON(), nullable=False),
|
||||
sa.Column("source_identity", sa.JSON(), nullable=False),
|
||||
sa.Column("target_identity", sa.JSON(), nullable=False),
|
||||
sa.Column("source_data_target", sa.Text(), nullable=False),
|
||||
sa.Column("target_shadow_target", sa.Text(), nullable=False),
|
||||
sa.Column("source_space_ref", sa.String(255), nullable=False),
|
||||
sa.Column(
|
||||
"target_space_id",
|
||||
sa.Uuid(),
|
||||
sa.ForeignKey("embedding_spaces.id", ondelete="RESTRICT"),
|
||||
nullable=False,
|
||||
),
|
||||
sa.Column("corpus_revision", sa.String(128), nullable=False),
|
||||
sa.Column("migration_policy_revision", sa.String(128), nullable=False),
|
||||
sa.Column(
|
||||
"validation_policy_revision_id",
|
||||
sa.Uuid(),
|
||||
sa.ForeignKey("migration_validation_policy_revisions.id", ondelete="RESTRICT"),
|
||||
nullable=False,
|
||||
),
|
||||
sa.Column(
|
||||
"lifecycle_approval_id",
|
||||
sa.Uuid(),
|
||||
sa.ForeignKey("lifecycle_approval_requests.id", ondelete="RESTRICT"),
|
||||
nullable=False,
|
||||
),
|
||||
sa.Column(
|
||||
"promotion_plan_id",
|
||||
sa.Uuid(),
|
||||
sa.ForeignKey("lifecycle_promotion_plans.id", ondelete="RESTRICT"),
|
||||
),
|
||||
sa.Column("rollback_target_ref", sa.Text(), nullable=False),
|
||||
sa.Column("total_expected_items", sa.Integer(), nullable=False),
|
||||
sa.Column("batch_size", sa.Integer(), nullable=False),
|
||||
sa.Column("max_in_flight_batches", sa.Integer(), nullable=False),
|
||||
sa.Column("concurrency", sa.Integer(), nullable=False),
|
||||
sa.Column("priority", sa.String(32), nullable=False),
|
||||
sa.Column("target_storage", sa.JSON(), nullable=False),
|
||||
sa.Column("shadow_policy", sa.JSON(), nullable=False),
|
||||
sa.Column("cutover_policy", sa.JSON(), nullable=False),
|
||||
sa.Column("rollback_retention_days", sa.Integer(), nullable=False),
|
||||
sa.Column("environment_fingerprint", sa.String(64), nullable=False),
|
||||
sa.Column("idempotency_key", sa.String(128), nullable=False),
|
||||
sa.Column("created_by", sa.String(255), nullable=False),
|
||||
sa.Column("irreversible", sa.Boolean(), nullable=False, server_default=sa.false()),
|
||||
sa.Column("schema_steps", sa.JSON(), nullable=False),
|
||||
sa.Column("state", sa.String(48), nullable=False, server_default="PLANNED"),
|
||||
sa.Column("version", sa.Integer(), nullable=False, server_default="1"),
|
||||
sa.Column("generation", sa.BigInteger(), nullable=False, server_default="1"),
|
||||
sa.Column("plan_fingerprint", sa.String(64), nullable=False),
|
||||
sa.Column("approval_fingerprint", sa.String(64), nullable=False),
|
||||
sa.Column("completed_items", sa.Integer(), nullable=False, server_default="0"),
|
||||
sa.Column("failed_items", sa.Integer(), nullable=False, server_default="0"),
|
||||
sa.Column("retryable_items", sa.Integer(), nullable=False, server_default="0"),
|
||||
sa.Column("permanent_failed_items", sa.Integer(), nullable=False, server_default="0"),
|
||||
sa.Column("last_cursor", sa.String(255)),
|
||||
sa.Column("cancel_requested", sa.Boolean(), nullable=False, server_default=sa.false()),
|
||||
sa.Column("failure_code", sa.String(64)),
|
||||
sa.Column("failure_details", sa.JSON(), nullable=False),
|
||||
sa.Column("started_at", sa.DateTime(timezone=True)),
|
||||
sa.Column("completed_at", sa.DateTime(timezone=True)),
|
||||
sa.Column("immutable_at", sa.DateTime(timezone=True), server_default=sa.func.now()),
|
||||
_created(),
|
||||
sa.Column("updated_at", sa.DateTime(timezone=True), server_default=sa.func.now()),
|
||||
sa.UniqueConstraint("idempotency_key", name="uq_migration_plan_idempotency"),
|
||||
sa.UniqueConstraint("plan_fingerprint", name="uq_migration_plan_fingerprint"),
|
||||
sa.UniqueConstraint("target_shadow_target", name="uq_migration_plans_target_shadow_target"),
|
||||
sa.CheckConstraint("version >= 1", name="ck_migration_plan_version"),
|
||||
sa.CheckConstraint("generation >= 1", name="ck_migration_plan_generation"),
|
||||
sa.CheckConstraint(
|
||||
"completed_items >= 0 AND failed_items >= 0", name="ck_migration_plan_progress"
|
||||
),
|
||||
)
|
||||
for column in (
|
||||
"project_id",
|
||||
"project_binding_id",
|
||||
"capability_contract_id",
|
||||
"migration_class",
|
||||
"environment",
|
||||
"target_space_id",
|
||||
"corpus_revision",
|
||||
"validation_policy_revision_id",
|
||||
"lifecycle_approval_id",
|
||||
"promotion_plan_id",
|
||||
"state",
|
||||
):
|
||||
op.create_index(f"ix_migration_plans_{column}", "migration_plans", [column])
|
||||
op.create_index(
|
||||
"uq_migration_one_production_active",
|
||||
"migration_plans",
|
||||
["project_id", "capability_contract_id"],
|
||||
unique=True,
|
||||
postgresql_where=sa.text(
|
||||
"environment = 'PRODUCTION' AND state NOT IN "
|
||||
"('CUTOVER_COMMITTED','ROLLED_BACK','FAILED','CANCELLED')"
|
||||
),
|
||||
)
|
||||
|
||||
op.create_table(
|
||||
"migration_batch_checkpoints",
|
||||
_id(),
|
||||
sa.Column(
|
||||
"migration_plan_id",
|
||||
sa.Uuid(),
|
||||
sa.ForeignKey("migration_plans.id", ondelete="RESTRICT"),
|
||||
nullable=False,
|
||||
),
|
||||
sa.Column("batch_number", sa.Integer(), nullable=False),
|
||||
sa.Column("generation", sa.BigInteger(), nullable=False),
|
||||
sa.Column("cursor_start", sa.String(255), nullable=False),
|
||||
sa.Column("cursor_end", sa.String(255), nullable=False),
|
||||
sa.Column("item_count", sa.Integer(), nullable=False),
|
||||
sa.Column("completed_items", sa.Integer(), nullable=False, server_default="0"),
|
||||
sa.Column("failed_items", sa.Integer(), nullable=False, server_default="0"),
|
||||
sa.Column("retryable_items", sa.Integer(), nullable=False, server_default="0"),
|
||||
sa.Column("permanent_failed_items", sa.Integer(), nullable=False, server_default="0"),
|
||||
sa.Column("item_fingerprint", sa.String(64), nullable=False),
|
||||
sa.Column("result_fingerprint", sa.String(64)),
|
||||
sa.Column("status", sa.String(32), nullable=False),
|
||||
sa.Column("attempts", sa.Integer(), nullable=False, server_default="0"),
|
||||
sa.Column("duration_ms", sa.Float()),
|
||||
sa.Column("error_code", sa.String(64)),
|
||||
sa.Column("bounded_errors", sa.JSON(), nullable=False),
|
||||
sa.Column("started_at", sa.DateTime(timezone=True)),
|
||||
sa.Column("completed_at", sa.DateTime(timezone=True)),
|
||||
sa.UniqueConstraint(
|
||||
"migration_plan_id", "batch_number", "generation", name="uq_migration_batch"
|
||||
),
|
||||
)
|
||||
op.create_index(
|
||||
"ix_migration_batch_checkpoints_migration_plan_id",
|
||||
"migration_batch_checkpoints",
|
||||
["migration_plan_id"],
|
||||
)
|
||||
op.create_index(
|
||||
"ix_migration_batch_checkpoints_status", "migration_batch_checkpoints", ["status"]
|
||||
)
|
||||
|
||||
op.create_table(
|
||||
"migration_validation_snapshots",
|
||||
_id(),
|
||||
sa.Column(
|
||||
"migration_plan_id",
|
||||
sa.Uuid(),
|
||||
sa.ForeignKey("migration_plans.id", ondelete="RESTRICT"),
|
||||
nullable=False,
|
||||
),
|
||||
sa.Column(
|
||||
"validation_policy_revision_id",
|
||||
sa.Uuid(),
|
||||
sa.ForeignKey("migration_validation_policy_revisions.id", ondelete="RESTRICT"),
|
||||
nullable=False,
|
||||
),
|
||||
sa.Column("generation", sa.BigInteger(), nullable=False),
|
||||
sa.Column("expected_count", sa.Integer(), nullable=False),
|
||||
sa.Column("actual_count", sa.Integer(), nullable=False),
|
||||
sa.Column("missing_count", sa.Integer(), nullable=False),
|
||||
sa.Column("duplicate_count", sa.Integer(), nullable=False),
|
||||
sa.Column("malformed_count", sa.Integer(), nullable=False),
|
||||
sa.Column("non_finite_count", sa.Integer(), nullable=False),
|
||||
sa.Column("wrong_dimension_count", sa.Integer(), nullable=False),
|
||||
sa.Column("content_hash_mismatch_count", sa.Integer(), nullable=False),
|
||||
sa.Column("wrong_space_count", sa.Integer(), nullable=False),
|
||||
sa.Column("index_schema_matches", sa.Boolean(), nullable=False),
|
||||
sa.Column("distance_metric_matches", sa.Boolean(), nullable=False),
|
||||
sa.Column("payload_integrity", sa.Boolean(), nullable=False),
|
||||
sa.Column("target_fingerprint", sa.String(64), nullable=False),
|
||||
sa.Column("evaluation_run_ids", sa.JSON(), nullable=False),
|
||||
sa.Column("comparable", sa.Boolean(), nullable=False),
|
||||
sa.Column("critical_regressions", sa.Integer(), nullable=False),
|
||||
sa.Column("latency_regression_ratio", sa.Float()),
|
||||
sa.Column("project_fit_eligible", sa.Boolean(), nullable=False),
|
||||
sa.Column("external_validation_satisfied", sa.Boolean(), nullable=False),
|
||||
sa.Column("security_approved", sa.Boolean(), nullable=False),
|
||||
sa.Column("evidence", sa.JSON(), nullable=False),
|
||||
sa.Column("snapshot_fingerprint", sa.String(64), nullable=False),
|
||||
sa.Column("passed", sa.Boolean(), nullable=False),
|
||||
sa.Column("technical_cutover_eligible", sa.Boolean(), nullable=False),
|
||||
sa.Column("project_promotion_eligible", sa.Boolean(), nullable=False),
|
||||
sa.Column("blockers", sa.JSON(), nullable=False),
|
||||
_created(),
|
||||
sa.Column("immutable_at", sa.DateTime(timezone=True), server_default=sa.func.now()),
|
||||
sa.UniqueConstraint("snapshot_fingerprint", name="uq_migration_validation_snapshot"),
|
||||
)
|
||||
op.create_index(
|
||||
"ix_migration_validation_snapshots_migration_plan_id",
|
||||
"migration_validation_snapshots",
|
||||
["migration_plan_id"],
|
||||
)
|
||||
op.create_index(
|
||||
"ix_migration_validation_snapshots_validation_policy_revision_id",
|
||||
"migration_validation_snapshots",
|
||||
["validation_policy_revision_id"],
|
||||
)
|
||||
op.create_index(
|
||||
"ix_migration_validation_snapshots_passed", "migration_validation_snapshots", ["passed"]
|
||||
)
|
||||
|
||||
op.create_table(
|
||||
"migration_shadow_sessions",
|
||||
_id(),
|
||||
sa.Column(
|
||||
"migration_plan_id",
|
||||
sa.Uuid(),
|
||||
sa.ForeignKey("migration_plans.id", ondelete="RESTRICT"),
|
||||
nullable=False,
|
||||
),
|
||||
sa.Column("generation", sa.BigInteger(), nullable=False),
|
||||
sa.Column("status", sa.String(32), nullable=False),
|
||||
sa.Column("request_count", sa.Integer(), nullable=False),
|
||||
sa.Column("source_error_count", sa.Integer(), nullable=False),
|
||||
sa.Column("target_error_count", sa.Integer(), nullable=False),
|
||||
sa.Column("source_latency_p95_ms", sa.Float(), nullable=False),
|
||||
sa.Column("target_latency_p95_ms", sa.Float(), nullable=False),
|
||||
sa.Column("critical_regressions", sa.Integer(), nullable=False),
|
||||
sa.Column("metrics", sa.JSON(), nullable=False),
|
||||
sa.Column("evidence_refs", sa.JSON(), nullable=False),
|
||||
sa.Column("result_fingerprint", sa.String(64), nullable=False),
|
||||
sa.Column("started_at", sa.DateTime(timezone=True), server_default=sa.func.now()),
|
||||
sa.Column("completed_at", sa.DateTime(timezone=True)),
|
||||
sa.UniqueConstraint("result_fingerprint", name="uq_migration_shadow_result_fingerprint"),
|
||||
)
|
||||
op.create_index(
|
||||
"ix_migration_shadow_sessions_migration_plan_id",
|
||||
"migration_shadow_sessions",
|
||||
["migration_plan_id"],
|
||||
)
|
||||
op.create_index("ix_migration_shadow_sessions_status", "migration_shadow_sessions", ["status"])
|
||||
|
||||
op.create_table(
|
||||
"migration_rollback_snapshots",
|
||||
_id(),
|
||||
sa.Column(
|
||||
"migration_plan_id",
|
||||
sa.Uuid(),
|
||||
sa.ForeignKey("migration_plans.id", ondelete="RESTRICT"),
|
||||
nullable=False,
|
||||
),
|
||||
sa.Column("snapshot", sa.JSON(), nullable=False),
|
||||
sa.Column("snapshot_fingerprint", sa.String(64), nullable=False),
|
||||
sa.Column("retain_until", sa.DateTime(timezone=True), nullable=False),
|
||||
_created(),
|
||||
sa.Column("immutable_at", sa.DateTime(timezone=True), server_default=sa.func.now()),
|
||||
sa.UniqueConstraint("migration_plan_id", name="uq_migration_rollback_plan"),
|
||||
sa.UniqueConstraint("snapshot_fingerprint", name="uq_migration_rollback_fingerprint"),
|
||||
)
|
||||
op.create_index(
|
||||
"ix_migration_rollback_snapshots_migration_plan_id",
|
||||
"migration_rollback_snapshots",
|
||||
["migration_plan_id"],
|
||||
)
|
||||
op.create_index(
|
||||
"ix_migration_rollback_snapshots_retain_until",
|
||||
"migration_rollback_snapshots",
|
||||
["retain_until"],
|
||||
)
|
||||
|
||||
op.create_table(
|
||||
"migration_cutover_operations",
|
||||
_id(),
|
||||
sa.Column(
|
||||
"migration_plan_id",
|
||||
sa.Uuid(),
|
||||
sa.ForeignKey("migration_plans.id", ondelete="RESTRICT"),
|
||||
nullable=False,
|
||||
),
|
||||
sa.Column("stage", sa.String(32), nullable=False),
|
||||
sa.Column("idempotency_key", sa.String(128), nullable=False),
|
||||
sa.Column("generation", sa.BigInteger(), nullable=False),
|
||||
sa.Column("expected_plan_version", sa.Integer(), nullable=False),
|
||||
sa.Column("source_before", sa.Text(), nullable=False),
|
||||
sa.Column("target_after", sa.Text(), nullable=False),
|
||||
sa.Column("external_state_fingerprint", sa.String(64), nullable=False),
|
||||
sa.Column("health_evidence", sa.JSON(), nullable=False),
|
||||
sa.Column("failure_code", sa.String(64)),
|
||||
sa.Column("failure_details", sa.JSON(), nullable=False),
|
||||
sa.Column("switch_duration_ms", sa.Float()),
|
||||
sa.Column("rollback_duration_ms", sa.Float()),
|
||||
sa.Column("started_at", sa.DateTime(timezone=True), server_default=sa.func.now()),
|
||||
sa.Column("finished_at", sa.DateTime(timezone=True)),
|
||||
sa.UniqueConstraint("idempotency_key", name="uq_migration_cutover_idempotency"),
|
||||
)
|
||||
op.create_index(
|
||||
"ix_migration_cutover_operations_migration_plan_id",
|
||||
"migration_cutover_operations",
|
||||
["migration_plan_id"],
|
||||
)
|
||||
op.create_index(
|
||||
"ix_migration_cutover_operations_stage", "migration_cutover_operations", ["stage"]
|
||||
)
|
||||
|
||||
op.create_table(
|
||||
"migration_events",
|
||||
_id(),
|
||||
sa.Column(
|
||||
"migration_plan_id",
|
||||
sa.Uuid(),
|
||||
sa.ForeignKey("migration_plans.id", ondelete="RESTRICT"),
|
||||
nullable=False,
|
||||
),
|
||||
sa.Column(
|
||||
"operation_id",
|
||||
sa.Uuid(),
|
||||
sa.ForeignKey("migration_cutover_operations.id", ondelete="RESTRICT"),
|
||||
),
|
||||
sa.Column("event_type", sa.String(64), nullable=False),
|
||||
sa.Column("before_state", sa.String(48)),
|
||||
sa.Column("after_state", sa.String(48)),
|
||||
sa.Column("actor", sa.String(255), nullable=False),
|
||||
sa.Column("policy_revision", sa.String(128), nullable=False),
|
||||
sa.Column("evidence_refs", sa.JSON(), nullable=False),
|
||||
sa.Column("reason", sa.Text(), nullable=False),
|
||||
sa.Column("source_identity", sa.JSON(), nullable=False),
|
||||
sa.Column("target_identity", sa.JSON(), nullable=False),
|
||||
sa.Column("generation", sa.BigInteger(), nullable=False),
|
||||
sa.Column("change_id", sa.String(128), nullable=False),
|
||||
sa.Column("details", sa.JSON(), nullable=False),
|
||||
sa.Column("occurred_at", sa.DateTime(timezone=True), server_default=sa.func.now()),
|
||||
)
|
||||
for column in ("migration_plan_id", "operation_id", "event_type", "change_id", "occurred_at"):
|
||||
op.create_index(f"ix_migration_events_{column}", "migration_events", [column])
|
||||
|
||||
|
||||
def downgrade() -> None:
|
||||
op.drop_table("migration_events")
|
||||
op.drop_table("migration_cutover_operations")
|
||||
op.drop_table("migration_rollback_snapshots")
|
||||
op.drop_table("migration_shadow_sessions")
|
||||
op.drop_table("migration_validation_snapshots")
|
||||
op.drop_table("migration_batch_checkpoints")
|
||||
op.drop_table("migration_plans")
|
||||
op.drop_table("migration_validation_policy_revisions")
|
||||
@@ -0,0 +1,257 @@
|
||||
"""Add operational SLO, alert and bounded capacity history records.
|
||||
|
||||
Revision ID: 20260827_0020
|
||||
Revises: 20260826_0019
|
||||
"""
|
||||
|
||||
from collections.abc import Sequence
|
||||
|
||||
import sqlalchemy as sa
|
||||
|
||||
from alembic import op
|
||||
|
||||
revision: str = "20260827_0020"
|
||||
down_revision: str | None = "20260826_0019"
|
||||
branch_labels: str | Sequence[str] | None = None
|
||||
depends_on: str | Sequence[str] | None = None
|
||||
|
||||
|
||||
def _id() -> sa.Column[object]:
|
||||
return sa.Column("id", sa.Uuid(), primary_key=True, nullable=False)
|
||||
|
||||
|
||||
def _created() -> sa.Column[object]:
|
||||
return sa.Column("created_at", sa.DateTime(timezone=True), server_default=sa.func.now())
|
||||
|
||||
|
||||
def upgrade() -> None:
|
||||
op.create_table(
|
||||
"service_level_indicators",
|
||||
_id(),
|
||||
sa.Column("key", sa.String(128), nullable=False, unique=True),
|
||||
sa.Column("name", sa.String(255), nullable=False),
|
||||
sa.Column("service", sa.String(128), nullable=False),
|
||||
sa.Column("capability", sa.String(128)),
|
||||
sa.Column("measurement", sa.String(32), nullable=False),
|
||||
sa.Column("valid_population", sa.JSON(), nullable=False),
|
||||
sa.Column("success_condition", sa.JSON(), nullable=False),
|
||||
sa.Column("default_window_seconds", sa.Integer(), nullable=False),
|
||||
sa.Column("enabled", sa.Boolean(), nullable=False, server_default=sa.true()),
|
||||
_created(),
|
||||
)
|
||||
for column in ("key", "service", "capability", "enabled"):
|
||||
op.create_index(f"ix_service_level_indicators_{column}", "service_level_indicators", [column])
|
||||
|
||||
op.create_table(
|
||||
"slo_policy_revisions",
|
||||
_id(),
|
||||
sa.Column("key", sa.String(128), nullable=False),
|
||||
sa.Column("revision", sa.Integer(), nullable=False),
|
||||
sa.Column("sli_definition_id", sa.Uuid(), sa.ForeignKey("service_level_indicators.id", ondelete="RESTRICT"), nullable=False),
|
||||
sa.Column("objective", sa.Float(), nullable=False),
|
||||
sa.Column("threshold_ms", sa.Float()),
|
||||
sa.Column("rolling_window_seconds", sa.Integer(), nullable=False),
|
||||
sa.Column("minimum_sample_count", sa.Integer(), nullable=False),
|
||||
sa.Column("severity", sa.String(16), nullable=False),
|
||||
sa.Column("environment", sa.String(16), nullable=False),
|
||||
sa.Column("rationale", sa.Text(), nullable=False),
|
||||
sa.Column("effective_from", sa.DateTime(timezone=True), nullable=False),
|
||||
sa.Column("active", sa.Boolean(), nullable=False, server_default=sa.true()),
|
||||
sa.Column("fingerprint", sa.String(64), nullable=False, unique=True),
|
||||
sa.Column("created_by", sa.String(255), nullable=False),
|
||||
_created(),
|
||||
sa.Column("immutable_at", sa.DateTime(timezone=True), server_default=sa.func.now()),
|
||||
sa.UniqueConstraint("key", "revision", name="uq_slo_policy_key_revision"),
|
||||
)
|
||||
for column in ("key", "sli_definition_id", "environment", "active"):
|
||||
op.create_index(f"ix_slo_policy_revisions_{column}", "slo_policy_revisions", [column])
|
||||
|
||||
op.create_table(
|
||||
"slo_evaluations",
|
||||
_id(),
|
||||
sa.Column("policy_id", sa.Uuid(), sa.ForeignKey("slo_policy_revisions.id", ondelete="RESTRICT"), nullable=False),
|
||||
sa.Column("window_start", sa.DateTime(timezone=True), nullable=False),
|
||||
sa.Column("window_end", sa.DateTime(timezone=True), nullable=False),
|
||||
sa.Column("observed_value", sa.Float()),
|
||||
sa.Column("sample_count", sa.Integer(), nullable=False),
|
||||
sa.Column("good_count", sa.Integer(), nullable=False),
|
||||
sa.Column("bad_count", sa.Integer(), nullable=False),
|
||||
sa.Column("state", sa.String(32), nullable=False),
|
||||
sa.Column("freshness_seconds", sa.Float()),
|
||||
sa.Column("allowed_bad", sa.Float()),
|
||||
sa.Column("consumed_bad", sa.Integer()),
|
||||
sa.Column("remaining_bad", sa.Float()),
|
||||
sa.Column("short_burn_rate", sa.Float()),
|
||||
sa.Column("long_burn_rate", sa.Float()),
|
||||
sa.Column("evidence", sa.JSON(), nullable=False),
|
||||
sa.Column("observed_at", sa.DateTime(timezone=True), server_default=sa.func.now()),
|
||||
sa.UniqueConstraint("policy_id", "window_end", name="uq_slo_evaluation_window"),
|
||||
)
|
||||
for column in ("policy_id", "window_start", "window_end", "state"):
|
||||
op.create_index(f"ix_slo_evaluations_{column}", "slo_evaluations", [column])
|
||||
|
||||
op.create_table(
|
||||
"alert_rule_revisions",
|
||||
_id(),
|
||||
sa.Column("key", sa.String(128), nullable=False),
|
||||
sa.Column("revision", sa.Integer(), nullable=False),
|
||||
sa.Column("alert_type", sa.String(64), nullable=False),
|
||||
sa.Column("signal", sa.String(128), nullable=False),
|
||||
sa.Column("slo_policy_id", sa.Uuid(), sa.ForeignKey("slo_policy_revisions.id", ondelete="RESTRICT")),
|
||||
sa.Column("condition", sa.JSON(), nullable=False),
|
||||
sa.Column("pending_seconds", sa.Integer(), nullable=False),
|
||||
sa.Column("severity", sa.String(16), nullable=False),
|
||||
sa.Column("labels", sa.JSON(), nullable=False),
|
||||
sa.Column("cooldown_seconds", sa.Integer(), nullable=False),
|
||||
sa.Column("recovery_condition", sa.JSON(), nullable=False),
|
||||
sa.Column("active", sa.Boolean(), nullable=False, server_default=sa.true()),
|
||||
sa.Column("fingerprint", sa.String(64), nullable=False, unique=True),
|
||||
sa.Column("created_by", sa.String(255), nullable=False),
|
||||
_created(),
|
||||
sa.Column("immutable_at", sa.DateTime(timezone=True), server_default=sa.func.now()),
|
||||
sa.UniqueConstraint("key", "revision", name="uq_alert_rule_key_revision"),
|
||||
)
|
||||
for column in ("key", "alert_type", "slo_policy_id", "severity", "active"):
|
||||
op.create_index(f"ix_alert_rule_revisions_{column}", "alert_rule_revisions", [column])
|
||||
|
||||
op.create_table(
|
||||
"maintenance_windows",
|
||||
_id(),
|
||||
sa.Column("name", sa.String(255), nullable=False),
|
||||
sa.Column("starts_at", sa.DateTime(timezone=True), nullable=False),
|
||||
sa.Column("ends_at", sa.DateTime(timezone=True), nullable=False),
|
||||
sa.Column("matcher", sa.JSON(), nullable=False),
|
||||
sa.Column("reason", sa.Text(), nullable=False),
|
||||
sa.Column("active", sa.Boolean(), nullable=False, server_default=sa.true()),
|
||||
sa.Column("created_by", sa.String(255), nullable=False),
|
||||
_created(),
|
||||
)
|
||||
for column in ("starts_at", "ends_at", "active"):
|
||||
op.create_index(f"ix_maintenance_windows_{column}", "maintenance_windows", [column])
|
||||
|
||||
op.create_table(
|
||||
"operational_incidents",
|
||||
_id(),
|
||||
sa.Column("fingerprint", sa.String(64), nullable=False, unique=True),
|
||||
sa.Column("title", sa.String(255), nullable=False),
|
||||
sa.Column("state", sa.String(32), nullable=False),
|
||||
sa.Column("severity", sa.String(16), nullable=False),
|
||||
sa.Column("root_subject_type", sa.String(64), nullable=False),
|
||||
sa.Column("root_subject_ref", sa.String(255), nullable=False),
|
||||
sa.Column("correlation", sa.String(32), nullable=False),
|
||||
sa.Column("first_seen_at", sa.DateTime(timezone=True), nullable=False),
|
||||
sa.Column("last_seen_at", sa.DateTime(timezone=True), nullable=False),
|
||||
sa.Column("resolved_at", sa.DateTime(timezone=True)),
|
||||
)
|
||||
op.create_index("ix_operational_incidents_state", "operational_incidents", ["state"])
|
||||
|
||||
op.create_table(
|
||||
"operational_alerts",
|
||||
_id(),
|
||||
sa.Column("rule_id", sa.Uuid(), sa.ForeignKey("alert_rule_revisions.id", ondelete="RESTRICT"), nullable=False),
|
||||
sa.Column("fingerprint", sa.String(64), nullable=False, unique=True),
|
||||
sa.Column("alert_type", sa.String(64), nullable=False),
|
||||
sa.Column("severity", sa.String(16), nullable=False),
|
||||
sa.Column("state", sa.String(32), nullable=False),
|
||||
sa.Column("source", sa.String(128), nullable=False),
|
||||
sa.Column("subject_type", sa.String(64), nullable=False),
|
||||
sa.Column("subject_ref", sa.String(255), nullable=False),
|
||||
sa.Column("summary", sa.Text(), nullable=False),
|
||||
sa.Column("details", sa.JSON(), nullable=False),
|
||||
sa.Column("first_seen_at", sa.DateTime(timezone=True), nullable=False),
|
||||
sa.Column("last_seen_at", sa.DateTime(timezone=True), nullable=False),
|
||||
sa.Column("firing_at", sa.DateTime(timezone=True)),
|
||||
sa.Column("acknowledged_at", sa.DateTime(timezone=True)),
|
||||
sa.Column("acknowledged_by", sa.String(255)),
|
||||
sa.Column("acknowledgement_reason", sa.Text()),
|
||||
sa.Column("resolved_at", sa.DateTime(timezone=True)),
|
||||
sa.Column("suppressed_until", sa.DateTime(timezone=True)),
|
||||
sa.Column("occurrence_count", sa.Integer(), nullable=False, server_default="1"),
|
||||
sa.Column("cooldown_until", sa.DateTime(timezone=True)),
|
||||
sa.Column("incident_id", sa.Uuid(), sa.ForeignKey("operational_incidents.id", ondelete="SET NULL")),
|
||||
)
|
||||
for column in ("rule_id", "alert_type", "severity", "state", "subject_ref", "first_seen_at", "last_seen_at", "incident_id"):
|
||||
op.create_index(f"ix_operational_alerts_{column}", "operational_alerts", [column])
|
||||
|
||||
op.create_table(
|
||||
"alert_history_events",
|
||||
_id(),
|
||||
sa.Column("alert_id", sa.Uuid(), sa.ForeignKey("operational_alerts.id", ondelete="RESTRICT"), nullable=False),
|
||||
sa.Column("from_state", sa.String(32)),
|
||||
sa.Column("to_state", sa.String(32), nullable=False),
|
||||
sa.Column("actor", sa.String(255), nullable=False),
|
||||
sa.Column("reason", sa.Text(), nullable=False),
|
||||
sa.Column("evidence", sa.JSON(), nullable=False),
|
||||
sa.Column("occurred_at", sa.DateTime(timezone=True), server_default=sa.func.now()),
|
||||
)
|
||||
for column in ("alert_id", "to_state", "occurred_at"):
|
||||
op.create_index(f"ix_alert_history_events_{column}", "alert_history_events", [column])
|
||||
|
||||
op.create_table(
|
||||
"incident_timeline_events",
|
||||
_id(),
|
||||
sa.Column("incident_id", sa.Uuid(), sa.ForeignKey("operational_incidents.id", ondelete="RESTRICT"), nullable=False),
|
||||
sa.Column("alert_id", sa.Uuid(), sa.ForeignKey("operational_alerts.id", ondelete="SET NULL")),
|
||||
sa.Column("event_type", sa.String(64), nullable=False),
|
||||
sa.Column("relation", sa.String(32), nullable=False),
|
||||
sa.Column("summary", sa.Text(), nullable=False),
|
||||
sa.Column("occurred_at", sa.DateTime(timezone=True), server_default=sa.func.now()),
|
||||
)
|
||||
for column in ("incident_id", "alert_id", "occurred_at"):
|
||||
op.create_index(f"ix_incident_timeline_events_{column}", "incident_timeline_events", [column])
|
||||
|
||||
op.create_table(
|
||||
"capacity_snapshots",
|
||||
_id(),
|
||||
sa.Column("observed_at", sa.DateTime(timezone=True), nullable=False),
|
||||
sa.Column("received_at", sa.DateTime(timezone=True), nullable=False),
|
||||
sa.Column("node_id", sa.Uuid(), sa.ForeignKey("compute_nodes.id", ondelete="RESTRICT"), nullable=False),
|
||||
sa.Column("node_name", sa.String(255), nullable=False),
|
||||
sa.Column("accelerator_id", sa.Uuid(), sa.ForeignKey("accelerators.id", ondelete="RESTRICT")),
|
||||
sa.Column("gpu_total_bytes", sa.BigInteger()),
|
||||
sa.Column("gpu_observed_bytes", sa.BigInteger()),
|
||||
sa.Column("gpu_external_bytes", sa.BigInteger()),
|
||||
sa.Column("gpu_managed_resident_bytes", sa.BigInteger()),
|
||||
sa.Column("gpu_leased_bytes", sa.BigInteger()),
|
||||
sa.Column("gpu_reserve_bytes", sa.BigInteger()),
|
||||
sa.Column("gpu_schedulable_bytes", sa.BigInteger()),
|
||||
sa.Column("pressure_state", sa.String(16), nullable=False),
|
||||
sa.Column("system_ram_total_bytes", sa.BigInteger()),
|
||||
sa.Column("system_ram_available_bytes", sa.BigInteger()),
|
||||
sa.Column("storage_total_bytes", sa.BigInteger()),
|
||||
sa.Column("storage_free_bytes", sa.BigInteger()),
|
||||
sa.Column("availability", sa.String(32), nullable=False),
|
||||
sa.Column("freshness_seconds", sa.Float(), nullable=False),
|
||||
sa.UniqueConstraint("node_id", "accelerator_id", "observed_at", name="uq_capacity_observation"),
|
||||
)
|
||||
for column in ("observed_at", "received_at", "node_id", "accelerator_id"):
|
||||
op.create_index(f"ix_capacity_snapshots_{column}", "capacity_snapshots", [column])
|
||||
|
||||
op.create_table(
|
||||
"capacity_aggregates",
|
||||
_id(),
|
||||
sa.Column("node_id", sa.Uuid(), sa.ForeignKey("compute_nodes.id", ondelete="RESTRICT"), nullable=False),
|
||||
sa.Column("accelerator_id", sa.Uuid(), sa.ForeignKey("accelerators.id", ondelete="RESTRICT")),
|
||||
sa.Column("bucket_start", sa.DateTime(timezone=True), nullable=False),
|
||||
sa.Column("bucket_seconds", sa.Integer(), nullable=False),
|
||||
sa.Column("sample_count", sa.Integer(), nullable=False),
|
||||
sa.Column("metrics", sa.JSON(), nullable=False),
|
||||
_created(),
|
||||
sa.UniqueConstraint("node_id", "accelerator_id", "bucket_start", name="uq_capacity_bucket"),
|
||||
)
|
||||
for column in ("node_id", "accelerator_id", "bucket_start"):
|
||||
op.create_index(f"ix_capacity_aggregates_{column}", "capacity_aggregates", [column])
|
||||
|
||||
|
||||
def downgrade() -> None:
|
||||
op.drop_table("capacity_aggregates")
|
||||
op.drop_table("capacity_snapshots")
|
||||
op.drop_table("incident_timeline_events")
|
||||
op.drop_table("alert_history_events")
|
||||
op.drop_table("operational_alerts")
|
||||
op.drop_table("operational_incidents")
|
||||
op.drop_table("maintenance_windows")
|
||||
op.drop_table("alert_rule_revisions")
|
||||
op.drop_table("slo_evaluations")
|
||||
op.drop_table("slo_policy_revisions")
|
||||
op.drop_table("service_level_indicators")
|
||||
@@ -0,0 +1,333 @@
|
||||
"""Add versioned recovery policies, immutable backup sets, restore journals and artifact recovery.
|
||||
|
||||
Revision ID: 20260827_0021
|
||||
Revises: 20260827_0020
|
||||
"""
|
||||
|
||||
from collections.abc import Sequence
|
||||
|
||||
import sqlalchemy as sa
|
||||
|
||||
from alembic import op
|
||||
|
||||
revision: str = "20260827_0021"
|
||||
down_revision: str | None = "20260827_0020"
|
||||
branch_labels: str | Sequence[str] | None = None
|
||||
depends_on: str | Sequence[str] | None = None
|
||||
|
||||
|
||||
def _id() -> sa.Column[object]:
|
||||
return sa.Column("id", sa.Uuid(), primary_key=True, nullable=False)
|
||||
|
||||
|
||||
def _created() -> sa.Column[object]:
|
||||
return sa.Column("created_at", sa.DateTime(timezone=True), server_default=sa.func.now())
|
||||
|
||||
|
||||
def upgrade() -> None:
|
||||
op.create_table(
|
||||
"recovery_policy_revisions",
|
||||
_id(),
|
||||
sa.Column("key", sa.String(128), nullable=False),
|
||||
sa.Column("revision", sa.Integer(), nullable=False),
|
||||
sa.Column("name", sa.String(255), nullable=False),
|
||||
sa.Column("asset_class", sa.String(32), nullable=False),
|
||||
sa.Column("backup_method", sa.String(48), nullable=False),
|
||||
sa.Column("retention_days", sa.Integer(), nullable=False),
|
||||
sa.Column("minimum_verified_backups", sa.Integer(), nullable=False),
|
||||
sa.Column("rpo_seconds", sa.Integer()),
|
||||
sa.Column("rto_target_seconds", sa.Integer()),
|
||||
sa.Column("restore_verification", sa.String(32), nullable=False),
|
||||
sa.Column("encryption_required", sa.Boolean(), nullable=False, server_default=sa.false()),
|
||||
sa.Column("external_dependency", sa.Boolean(), nullable=False, server_default=sa.false()),
|
||||
sa.Column("rehydration_allowed", sa.Boolean(), nullable=False, server_default=sa.false()),
|
||||
sa.Column("secret_class", sa.String(32)),
|
||||
sa.Column("rationale", sa.Text(), nullable=False),
|
||||
sa.Column("active", sa.Boolean(), nullable=False, server_default=sa.true()),
|
||||
sa.Column("fingerprint", sa.String(64), nullable=False, unique=True),
|
||||
sa.Column("created_by", sa.String(255), nullable=False),
|
||||
_created(),
|
||||
sa.Column("immutable_at", sa.DateTime(timezone=True), server_default=sa.func.now()),
|
||||
sa.UniqueConstraint("key", "revision", name="uq_recovery_policy_key_revision"),
|
||||
sa.CheckConstraint("retention_days >= 1", name="ck_recovery_policy_retention"),
|
||||
sa.CheckConstraint(
|
||||
"minimum_verified_backups >= 1", name="ck_recovery_policy_minimum_verified"
|
||||
),
|
||||
)
|
||||
for column in ("key", "asset_class", "active"):
|
||||
op.create_index(
|
||||
f"ix_recovery_policy_revisions_{column}", "recovery_policy_revisions", [column]
|
||||
)
|
||||
|
||||
op.create_table(
|
||||
"recovery_asset_records",
|
||||
_id(),
|
||||
sa.Column("key", sa.String(128), nullable=False, unique=True),
|
||||
sa.Column("name", sa.String(255), nullable=False),
|
||||
sa.Column("asset_class", sa.String(32), nullable=False),
|
||||
sa.Column("owner", sa.String(128), nullable=False),
|
||||
sa.Column("location", sa.Text(), nullable=False),
|
||||
sa.Column("backup_method", sa.String(48), nullable=False),
|
||||
sa.Column("restore_method", sa.Text(), nullable=False),
|
||||
sa.Column("rebuild_method", sa.Text()),
|
||||
sa.Column("rpo_seconds", sa.Integer()),
|
||||
sa.Column("readiness", sa.String(32), nullable=False),
|
||||
sa.Column("dependencies", sa.JSON(), nullable=False),
|
||||
sa.Column("notes", sa.Text(), nullable=False, server_default=""),
|
||||
sa.Column(
|
||||
"policy_revision_id",
|
||||
sa.Uuid(),
|
||||
sa.ForeignKey("recovery_policy_revisions.id", ondelete="RESTRICT"),
|
||||
nullable=False,
|
||||
),
|
||||
_created(),
|
||||
sa.Column("updated_at", sa.DateTime(timezone=True), server_default=sa.func.now()),
|
||||
)
|
||||
for column in ("key", "asset_class", "readiness", "policy_revision_id"):
|
||||
op.create_index(f"ix_recovery_asset_records_{column}", "recovery_asset_records", [column])
|
||||
|
||||
op.create_table(
|
||||
"backup_sets",
|
||||
_id(),
|
||||
sa.Column("backup_id", sa.String(64), nullable=False),
|
||||
sa.Column("state", sa.String(32), nullable=False, server_default="PLANNED"),
|
||||
sa.Column(
|
||||
"policy_revision_id",
|
||||
sa.Uuid(),
|
||||
sa.ForeignKey("recovery_policy_revisions.id", ondelete="RESTRICT"),
|
||||
nullable=False,
|
||||
),
|
||||
sa.Column("modelforge_version", sa.String(64), nullable=False),
|
||||
sa.Column("modelforge_commit", sa.String(64)),
|
||||
sa.Column("source_repository", sa.Text()),
|
||||
sa.Column("source_reference", sa.String(255)),
|
||||
sa.Column("schema_revision", sa.String(64)),
|
||||
sa.Column("environment_fingerprint", sa.JSON(), nullable=False),
|
||||
sa.Column("database_identity", sa.JSON(), nullable=False),
|
||||
sa.Column("destination_root", sa.Text(), nullable=False),
|
||||
sa.Column("payload_relative_path", sa.Text()),
|
||||
sa.Column("payload_sha256", sa.String(64)),
|
||||
sa.Column("manifest_relative_path", sa.Text()),
|
||||
sa.Column("manifest_sha256", sa.String(64)),
|
||||
sa.Column("included_asset_classes", sa.JSON(), nullable=False),
|
||||
sa.Column("excluded_asset_classes", sa.JSON(), nullable=False),
|
||||
sa.Column("payload_bytes", sa.BigInteger(), nullable=False, server_default="0"),
|
||||
sa.Column("encrypted", sa.Boolean(), nullable=False, server_default=sa.false()),
|
||||
sa.Column("encryption_algorithm", sa.String(64)),
|
||||
sa.Column("encryption_key_id", sa.String(128)),
|
||||
sa.Column("verification_details", sa.JSON(), nullable=False),
|
||||
sa.Column("verified_at", sa.DateTime(timezone=True)),
|
||||
sa.Column("failure_code", sa.String(64)),
|
||||
sa.Column("failure_reason", sa.Text()),
|
||||
sa.Column("milestone", sa.String(64)),
|
||||
sa.Column("legal_hold", sa.Boolean(), nullable=False, server_default=sa.false()),
|
||||
sa.Column("reason", sa.Text(), nullable=False),
|
||||
sa.Column("created_by", sa.String(255), nullable=False),
|
||||
sa.Column("started_at", sa.DateTime(timezone=True)),
|
||||
sa.Column("completed_at", sa.DateTime(timezone=True)),
|
||||
sa.Column("expires_at", sa.DateTime(timezone=True)),
|
||||
_created(),
|
||||
sa.Column("immutable_at", sa.DateTime(timezone=True)),
|
||||
sa.UniqueConstraint("backup_id", name="uq_backup_set_backup_id"),
|
||||
sa.CheckConstraint("payload_bytes >= 0", name="ck_backup_set_payload_bytes"),
|
||||
)
|
||||
for column in (
|
||||
"backup_id",
|
||||
"state",
|
||||
"policy_revision_id",
|
||||
"schema_revision",
|
||||
"manifest_sha256",
|
||||
"verified_at",
|
||||
"milestone",
|
||||
"legal_hold",
|
||||
"expires_at",
|
||||
):
|
||||
op.create_index(f"ix_backup_sets_{column}", "backup_sets", [column])
|
||||
|
||||
op.create_table(
|
||||
"backup_manifest_entries",
|
||||
_id(),
|
||||
sa.Column(
|
||||
"backup_set_id",
|
||||
sa.Uuid(),
|
||||
sa.ForeignKey("backup_sets.id", ondelete="CASCADE"),
|
||||
nullable=False,
|
||||
),
|
||||
sa.Column("logical_asset_type", sa.String(64), nullable=False),
|
||||
sa.Column("object_name", sa.String(255), nullable=False),
|
||||
sa.Column("relative_path", sa.Text(), nullable=False),
|
||||
sa.Column("size_bytes", sa.BigInteger(), nullable=False),
|
||||
sa.Column("sha256", sa.String(64), nullable=False),
|
||||
sa.Column("source_generation", sa.String(128), nullable=False),
|
||||
sa.Column("schema_version", sa.String(64)),
|
||||
sa.Column("dependency_refs", sa.JSON(), nullable=False),
|
||||
_created(),
|
||||
sa.UniqueConstraint("backup_set_id", "object_name", name="uq_backup_manifest_object"),
|
||||
sa.CheckConstraint("length(sha256) = 64", name="ck_backup_manifest_sha256_length"),
|
||||
sa.CheckConstraint("size_bytes >= 0", name="ck_backup_manifest_size"),
|
||||
)
|
||||
for column in ("backup_set_id", "logical_asset_type"):
|
||||
op.create_index(
|
||||
f"ix_backup_manifest_entries_{column}", "backup_manifest_entries", [column]
|
||||
)
|
||||
|
||||
op.create_table(
|
||||
"restore_plans",
|
||||
_id(),
|
||||
sa.Column(
|
||||
"backup_set_id",
|
||||
sa.Uuid(),
|
||||
sa.ForeignKey("backup_sets.id", ondelete="RESTRICT"),
|
||||
nullable=False,
|
||||
),
|
||||
sa.Column("mode", sa.String(32), nullable=False),
|
||||
sa.Column("state", sa.String(32), nullable=False, server_default="DRAFT"),
|
||||
sa.Column("target_environment", sa.String(32), nullable=False),
|
||||
sa.Column("target_label", sa.String(128), nullable=False),
|
||||
sa.Column("database_destination", sa.Text(), nullable=False),
|
||||
sa.Column("artifact_strategy", sa.String(32), nullable=False),
|
||||
sa.Column("secret_strategy", sa.String(32), nullable=False),
|
||||
sa.Column("node_strategy", sa.String(32), nullable=False),
|
||||
sa.Column("expected_modelforge_version", sa.String(64)),
|
||||
sa.Column("preflight", sa.JSON(), nullable=False),
|
||||
sa.Column("validation_requirements", sa.JSON(), nullable=False),
|
||||
sa.Column("fingerprint", sa.String(64), nullable=False),
|
||||
sa.Column("reason", sa.Text(), nullable=False),
|
||||
sa.Column("created_by", sa.String(255), nullable=False),
|
||||
_created(),
|
||||
sa.UniqueConstraint("fingerprint", name="uq_restore_plan_fingerprint"),
|
||||
)
|
||||
for column in ("backup_set_id", "mode", "state", "target_environment"):
|
||||
op.create_index(f"ix_restore_plans_{column}", "restore_plans", [column])
|
||||
|
||||
op.create_table(
|
||||
"restore_operations",
|
||||
_id(),
|
||||
sa.Column(
|
||||
"plan_id",
|
||||
sa.Uuid(),
|
||||
sa.ForeignKey("restore_plans.id", ondelete="RESTRICT"),
|
||||
nullable=False,
|
||||
),
|
||||
sa.Column(
|
||||
"backup_set_id",
|
||||
sa.Uuid(),
|
||||
sa.ForeignKey("backup_sets.id", ondelete="RESTRICT"),
|
||||
nullable=False,
|
||||
),
|
||||
sa.Column("state", sa.String(48), nullable=False, server_default="PLANNED"),
|
||||
sa.Column("attempt", sa.Integer(), nullable=False, server_default="1"),
|
||||
sa.Column("idempotency_key", sa.String(64), nullable=False),
|
||||
sa.Column("preflight_result", sa.JSON(), nullable=False),
|
||||
sa.Column("phase_durations", sa.JSON(), nullable=False),
|
||||
sa.Column("source_fingerprint", sa.JSON(), nullable=False),
|
||||
sa.Column("restored_fingerprint", sa.JSON(), nullable=False),
|
||||
sa.Column("fingerprint_diff", sa.JSON(), nullable=False),
|
||||
sa.Column("validation_result", sa.JSON(), nullable=False),
|
||||
sa.Column("rpo_seconds", sa.Float()),
|
||||
sa.Column("rto_seconds", sa.Float()),
|
||||
sa.Column("failure_code", sa.String(64)),
|
||||
sa.Column("failure_reason", sa.Text()),
|
||||
sa.Column("started_at", sa.DateTime(timezone=True), server_default=sa.func.now()),
|
||||
sa.Column("updated_at", sa.DateTime(timezone=True), server_default=sa.func.now()),
|
||||
sa.Column("ready_at", sa.DateTime(timezone=True)),
|
||||
sa.UniqueConstraint("idempotency_key", name="uq_restore_operation_idempotency"),
|
||||
)
|
||||
for column in ("plan_id", "backup_set_id", "state"):
|
||||
op.create_index(f"ix_restore_operations_{column}", "restore_operations", [column])
|
||||
|
||||
op.create_table(
|
||||
"restore_operation_events",
|
||||
_id(),
|
||||
sa.Column(
|
||||
"restore_operation_id",
|
||||
sa.Uuid(),
|
||||
sa.ForeignKey("restore_operations.id", ondelete="CASCADE"),
|
||||
nullable=False,
|
||||
),
|
||||
sa.Column("from_state", sa.String(48)),
|
||||
sa.Column("to_state", sa.String(48), nullable=False),
|
||||
sa.Column("phase", sa.String(48), nullable=False),
|
||||
sa.Column("actor", sa.String(255), nullable=False),
|
||||
sa.Column("reason", sa.Text(), nullable=False),
|
||||
sa.Column("evidence", sa.JSON(), nullable=False),
|
||||
sa.Column("occurred_at", sa.DateTime(timezone=True), server_default=sa.func.now()),
|
||||
)
|
||||
for column in ("restore_operation_id", "to_state", "occurred_at"):
|
||||
op.create_index(
|
||||
f"ix_restore_operation_events_{column}", "restore_operation_events", [column]
|
||||
)
|
||||
|
||||
op.create_table(
|
||||
"artifact_recovery_operations",
|
||||
_id(),
|
||||
sa.Column(
|
||||
"restore_operation_id",
|
||||
sa.Uuid(),
|
||||
sa.ForeignKey("restore_operations.id", ondelete="SET NULL"),
|
||||
),
|
||||
sa.Column(
|
||||
"artifact_set_id",
|
||||
sa.Uuid(),
|
||||
sa.ForeignKey("artifact_sets.id", ondelete="RESTRICT"),
|
||||
nullable=False,
|
||||
),
|
||||
sa.Column(
|
||||
"model_revision_id",
|
||||
sa.Uuid(),
|
||||
sa.ForeignKey("model_revisions.id", ondelete="RESTRICT"),
|
||||
nullable=False,
|
||||
),
|
||||
sa.Column("recovery_class", sa.String(32), nullable=False),
|
||||
sa.Column("state", sa.String(32), nullable=False, server_default="PLANNED"),
|
||||
sa.Column("upstream_repository", sa.Text()),
|
||||
sa.Column("upstream_commit_sha", sa.String(64)),
|
||||
sa.Column(
|
||||
"target_storage_root_id",
|
||||
sa.Uuid(),
|
||||
sa.ForeignKey("storage_roots.id", ondelete="RESTRICT"),
|
||||
nullable=False,
|
||||
),
|
||||
sa.Column("expected_files", sa.JSON(), nullable=False),
|
||||
sa.Column("verified_files", sa.JSON(), nullable=False),
|
||||
sa.Column("bytes_total", sa.BigInteger(), nullable=False, server_default="0"),
|
||||
sa.Column("bytes_recovered", sa.BigInteger(), nullable=False, server_default="0"),
|
||||
sa.Column(
|
||||
"download_plan_id", sa.Uuid(), sa.ForeignKey("download_plans.id", ondelete="SET NULL")
|
||||
),
|
||||
sa.Column(
|
||||
"artifact_job_id", sa.Uuid(), sa.ForeignKey("artifact_jobs.id", ondelete="SET NULL")
|
||||
),
|
||||
sa.Column("lineage", sa.JSON(), nullable=False),
|
||||
sa.Column("duration_seconds", sa.Float()),
|
||||
sa.Column("failure_code", sa.String(64)),
|
||||
sa.Column("failure_reason", sa.Text()),
|
||||
sa.Column("reason", sa.Text(), nullable=False),
|
||||
sa.Column("created_by", sa.String(255), nullable=False),
|
||||
sa.Column("started_at", sa.DateTime(timezone=True), server_default=sa.func.now()),
|
||||
sa.Column("completed_at", sa.DateTime(timezone=True)),
|
||||
)
|
||||
for column in (
|
||||
"restore_operation_id",
|
||||
"artifact_set_id",
|
||||
"model_revision_id",
|
||||
"recovery_class",
|
||||
"state",
|
||||
"target_storage_root_id",
|
||||
"download_plan_id",
|
||||
"artifact_job_id",
|
||||
):
|
||||
op.create_index(
|
||||
f"ix_artifact_recovery_operations_{column}", "artifact_recovery_operations", [column]
|
||||
)
|
||||
|
||||
|
||||
def downgrade() -> None:
|
||||
op.drop_table("artifact_recovery_operations")
|
||||
op.drop_table("restore_operation_events")
|
||||
op.drop_table("restore_operations")
|
||||
op.drop_table("restore_plans")
|
||||
op.drop_table("backup_manifest_entries")
|
||||
op.drop_table("backup_sets")
|
||||
op.drop_table("recovery_asset_records")
|
||||
op.drop_table("recovery_policy_revisions")
|
||||
@@ -0,0 +1,71 @@
|
||||
"""Add audited compute-node decommission tombstones and operation records.
|
||||
|
||||
Revision ID: 20260828_0022
|
||||
Revises: 20260827_0021
|
||||
"""
|
||||
|
||||
from collections.abc import Sequence
|
||||
|
||||
import sqlalchemy as sa
|
||||
|
||||
from alembic import op
|
||||
|
||||
revision: str = "20260828_0022"
|
||||
down_revision: str | None = "20260827_0021"
|
||||
branch_labels: str | Sequence[str] | None = None
|
||||
depends_on: str | Sequence[str] | None = None
|
||||
|
||||
|
||||
def upgrade() -> None:
|
||||
op.add_column(
|
||||
"compute_nodes",
|
||||
sa.Column("generation", sa.BigInteger(), nullable=False, server_default="1"),
|
||||
)
|
||||
op.add_column("compute_nodes", sa.Column("decommissioned_at", sa.DateTime(timezone=True)))
|
||||
op.add_column("compute_nodes", sa.Column("decommission_reason", sa.Text()))
|
||||
op.add_column("compute_nodes", sa.Column("decommissioned_by", sa.String(255)))
|
||||
op.create_index("ix_compute_nodes_decommissioned_at", "compute_nodes", ["decommissioned_at"])
|
||||
op.create_table(
|
||||
"node_decommission_operations",
|
||||
sa.Column("id", sa.Uuid(), primary_key=True, nullable=False),
|
||||
sa.Column(
|
||||
"compute_node_id",
|
||||
sa.Uuid(),
|
||||
sa.ForeignKey("compute_nodes.id", ondelete="RESTRICT"),
|
||||
nullable=False,
|
||||
),
|
||||
sa.Column("persisted_identity", sa.String(128), nullable=False),
|
||||
sa.Column("idempotency_key", sa.String(128), nullable=False),
|
||||
sa.Column("expected_generation", sa.BigInteger(), nullable=False),
|
||||
sa.Column("preview_digest", sa.String(64), nullable=False),
|
||||
sa.Column("reason", sa.Text(), nullable=False),
|
||||
sa.Column("operator", sa.String(255), nullable=False),
|
||||
sa.Column("previous_state", sa.JSON(), nullable=False),
|
||||
sa.Column("cleanup_summary", sa.JSON(), nullable=False),
|
||||
sa.Column("status", sa.String(32), nullable=False, server_default="completed"),
|
||||
sa.Column(
|
||||
"created_at",
|
||||
sa.DateTime(timezone=True),
|
||||
nullable=False,
|
||||
server_default=sa.func.now(),
|
||||
),
|
||||
sa.Column("decommissioned_at", sa.DateTime(timezone=True), nullable=False),
|
||||
sa.UniqueConstraint("compute_node_id", name="uq_node_decommission_node"),
|
||||
sa.UniqueConstraint("idempotency_key", name="uq_node_decommission_idempotency"),
|
||||
sa.CheckConstraint("expected_generation >= 1", name="ck_node_decommission_generation"),
|
||||
)
|
||||
for column in ("compute_node_id", "persisted_identity", "status", "decommissioned_at"):
|
||||
op.create_index(
|
||||
f"ix_node_decommission_operations_{column}",
|
||||
"node_decommission_operations",
|
||||
[column],
|
||||
)
|
||||
|
||||
|
||||
def downgrade() -> None:
|
||||
op.drop_table("node_decommission_operations")
|
||||
op.drop_index("ix_compute_nodes_decommissioned_at", table_name="compute_nodes")
|
||||
op.drop_column("compute_nodes", "decommissioned_by")
|
||||
op.drop_column("compute_nodes", "decommission_reason")
|
||||
op.drop_column("compute_nodes", "decommissioned_at")
|
||||
op.drop_column("compute_nodes", "generation")
|
||||
@@ -0,0 +1,56 @@
|
||||
"""Enforce the two fixed Node Agent credential scopes.
|
||||
|
||||
Revision ID: 20260830_0023
|
||||
Revises: 20260828_0022
|
||||
"""
|
||||
|
||||
from collections.abc import Sequence
|
||||
|
||||
import sqlalchemy as sa
|
||||
|
||||
from alembic import op
|
||||
|
||||
revision: str = "20260830_0023"
|
||||
down_revision: str | None = "20260828_0022"
|
||||
branch_labels: str | Sequence[str] | None = None
|
||||
depends_on: str | Sequence[str] | None = None
|
||||
|
||||
|
||||
_SCOPE_CONSTRAINTS = (
|
||||
("node_enrollments", "node.enroll", "ck_node_enrollment_scope"),
|
||||
("node_credentials", "node.publish", "ck_node_credential_scope"),
|
||||
)
|
||||
|
||||
|
||||
def _validate_existing_scopes() -> None:
|
||||
connection = op.get_bind()
|
||||
for table_name, expected_scope, _constraint_name in _SCOPE_CONSTRAINTS:
|
||||
table = sa.table(table_name, sa.column("scope", sa.String(64)))
|
||||
invalid_rows = connection.scalar(
|
||||
sa.select(sa.func.count())
|
||||
.select_from(table)
|
||||
.where(sa.or_(table.c.scope.is_(None), table.c.scope != expected_scope))
|
||||
)
|
||||
if invalid_rows:
|
||||
raise RuntimeError(
|
||||
f"refusing node-scope migration: {table_name} contains "
|
||||
f"{invalid_rows} row(s) outside {expected_scope!r}"
|
||||
)
|
||||
|
||||
|
||||
def upgrade() -> None:
|
||||
# Validate every table before changing either one. A malformed production row therefore aborts
|
||||
# the migration without leaving a partially hardened schema.
|
||||
_validate_existing_scopes()
|
||||
for table_name, expected_scope, constraint_name in _SCOPE_CONSTRAINTS:
|
||||
with op.batch_alter_table(table_name) as batch_op:
|
||||
batch_op.create_check_constraint(
|
||||
constraint_name,
|
||||
f"scope = '{expected_scope}'",
|
||||
)
|
||||
|
||||
|
||||
def downgrade() -> None:
|
||||
for table_name, _expected_scope, constraint_name in reversed(_SCOPE_CONSTRAINTS):
|
||||
with op.batch_alter_table(table_name) as batch_op:
|
||||
batch_op.drop_constraint(constraint_name, type_="check")
|
||||
@@ -0,0 +1,717 @@
|
||||
"""Version and checkpoint the tamper-evident audit chain.
|
||||
|
||||
Revision ID: 20260830_0024
|
||||
Revises: 20260830_0023
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import hashlib
|
||||
import json
|
||||
import re
|
||||
import uuid
|
||||
from datetime import UTC, datetime
|
||||
from typing import Any
|
||||
|
||||
import sqlalchemy as sa
|
||||
from sqlalchemy.engine import Connection
|
||||
|
||||
from alembic import op
|
||||
|
||||
revision = "20260830_0024"
|
||||
down_revision = "20260830_0023"
|
||||
branch_labels = None
|
||||
depends_on = None
|
||||
|
||||
_LEGACY_PREFIX_DOMAIN = b"modelforge:audit:legacy-prefix:v1\n"
|
||||
_SHA256 = re.compile(r"^[0-9a-f]{64}$")
|
||||
_AUDIT_CHAIN_LOCK_KEY = int.from_bytes(b"MF_AUDIT", byteorder="big", signed=False)
|
||||
_AUDIT_OWNER_ROLE = "modelforge"
|
||||
_AUDIT_RUNTIME_ROLE = "modelforge_runtime"
|
||||
|
||||
_POSTGRES_AUDIT_BOUNDARY_SQL = r"""
|
||||
create schema if not exists modelforge_audit authorization modelforge;
|
||||
alter schema modelforge_audit owner to modelforge;
|
||||
revoke all on schema modelforge_audit from public;
|
||||
revoke create on schema public from modelforge_runtime;
|
||||
grant usage on schema public, modelforge_audit to modelforge_runtime;
|
||||
|
||||
create or replace function modelforge_audit.enforce_owner_mutation()
|
||||
returns trigger
|
||||
language plpgsql
|
||||
security invoker
|
||||
set search_path = pg_catalog
|
||||
as $guard$
|
||||
begin
|
||||
if current_user <> 'modelforge' then
|
||||
raise exception 'audit tables are writable only through the canonical append function'
|
||||
using errcode = '42501';
|
||||
end if;
|
||||
if tg_op = 'DELETE' then
|
||||
return old;
|
||||
elsif tg_op = 'TRUNCATE' then
|
||||
return null;
|
||||
end if;
|
||||
return new;
|
||||
end
|
||||
$guard$;
|
||||
alter function modelforge_audit.enforce_owner_mutation() owner to modelforge;
|
||||
revoke all on function modelforge_audit.enforce_owner_mutation() from public;
|
||||
revoke all on function modelforge_audit.enforce_owner_mutation() from modelforge_runtime;
|
||||
|
||||
drop trigger if exists trg_modelforge_audit_events_owner on public.audit_events;
|
||||
create trigger trg_modelforge_audit_events_owner
|
||||
before insert or update or delete on public.audit_events
|
||||
for each row execute function modelforge_audit.enforce_owner_mutation();
|
||||
drop trigger if exists trg_modelforge_audit_events_truncate_owner on public.audit_events;
|
||||
create trigger trg_modelforge_audit_events_truncate_owner
|
||||
before truncate on public.audit_events
|
||||
for each statement execute function modelforge_audit.enforce_owner_mutation();
|
||||
drop trigger if exists trg_modelforge_audit_head_owner on public.audit_chain_heads;
|
||||
create trigger trg_modelforge_audit_head_owner
|
||||
before insert or update or delete on public.audit_chain_heads
|
||||
for each row execute function modelforge_audit.enforce_owner_mutation();
|
||||
drop trigger if exists trg_modelforge_audit_head_truncate_owner on public.audit_chain_heads;
|
||||
create trigger trg_modelforge_audit_head_truncate_owner
|
||||
before truncate on public.audit_chain_heads
|
||||
for each statement execute function modelforge_audit.enforce_owner_mutation();
|
||||
|
||||
create or replace function modelforge_audit.append_event_v2(
|
||||
p_event_id uuid,
|
||||
p_occurred_at timestamptz,
|
||||
p_correlation_id text,
|
||||
p_actor_type text,
|
||||
p_actor_id text,
|
||||
p_action text,
|
||||
p_resource_type text,
|
||||
p_resource_id text,
|
||||
p_outcome text,
|
||||
p_details jsonb,
|
||||
p_expected_event_count bigint,
|
||||
p_expected_last_sequence bigint,
|
||||
p_expected_last_event_hash text,
|
||||
p_expected_hash_format text,
|
||||
p_expected_v2_start_sequence bigint,
|
||||
p_expected_legacy_prefix_count bigint,
|
||||
p_expected_legacy_prefix_seal text
|
||||
)
|
||||
returns table(event_id uuid, sequence bigint, event_hash text, occurred_at timestamptz)
|
||||
language plpgsql
|
||||
security definer
|
||||
set search_path = pg_catalog
|
||||
as $append$
|
||||
declare
|
||||
v_head public.audit_chain_heads%rowtype;
|
||||
v_tail public.audit_events%rowtype;
|
||||
v_max_sequence bigint;
|
||||
v_predecessor_hash text;
|
||||
v_sequence bigint;
|
||||
v_payload text;
|
||||
v_hash text;
|
||||
v_updated bigint;
|
||||
begin
|
||||
perform pg_catalog.pg_advisory_xact_lock(5568242723498248532);
|
||||
|
||||
if p_event_id is null or p_occurred_at is null or p_correlation_id is null
|
||||
or p_actor_type is null or p_actor_id is null or p_action is null
|
||||
or p_resource_type is null or p_outcome is null or p_details is null then
|
||||
raise exception 'canonical audit append arguments must not be null'
|
||||
using errcode = '23502';
|
||||
end if;
|
||||
if not pg_catalog.isfinite(p_occurred_at) then
|
||||
raise exception 'canonical audit occurred_at must be finite' using errcode = '22008';
|
||||
end if;
|
||||
if pg_catalog.jsonb_typeof(p_details) <> 'object' then
|
||||
raise exception 'canonical audit details must be a JSON object'
|
||||
using errcode = '22023';
|
||||
end if;
|
||||
|
||||
select head.* into v_head
|
||||
from public.audit_chain_heads as head
|
||||
where head.singleton_id = 1
|
||||
for update;
|
||||
if not found then
|
||||
raise exception 'audit checkpoint is missing' using errcode = '23514';
|
||||
end if;
|
||||
if v_head.event_count is distinct from p_expected_event_count
|
||||
or v_head.last_sequence is distinct from p_expected_last_sequence
|
||||
or v_head.last_event_hash is distinct from p_expected_last_event_hash
|
||||
or v_head.hash_format is distinct from p_expected_hash_format
|
||||
or v_head.v2_start_sequence is distinct from p_expected_v2_start_sequence
|
||||
or v_head.legacy_prefix_count is distinct from p_expected_legacy_prefix_count
|
||||
or v_head.legacy_prefix_seal is distinct from p_expected_legacy_prefix_seal then
|
||||
raise exception 'audit checkpoint changed before canonical append'
|
||||
using errcode = '40001';
|
||||
end if;
|
||||
if v_head.hash_format <> 'v2'
|
||||
or v_head.event_count <> v_head.last_sequence
|
||||
or v_head.event_count < 0
|
||||
or v_head.v2_start_sequence < 1
|
||||
or v_head.legacy_prefix_count <> v_head.v2_start_sequence - 1
|
||||
or v_head.legacy_prefix_count > v_head.event_count
|
||||
or v_head.legacy_prefix_seal !~ '^[0-9a-f]{64}$' then
|
||||
raise exception 'audit checkpoint invariants are invalid' using errcode = '23514';
|
||||
end if;
|
||||
|
||||
select events.sequence into v_max_sequence
|
||||
from public.audit_events as events
|
||||
order by events.sequence desc, events.id desc
|
||||
limit 1;
|
||||
if v_head.event_count = 0 then
|
||||
if v_max_sequence is not null or v_head.last_event_hash is not null then
|
||||
raise exception 'empty audit checkpoint has retained events' using errcode = '23514';
|
||||
end if;
|
||||
else
|
||||
if v_max_sequence is distinct from v_head.last_sequence
|
||||
or v_head.last_event_hash is null then
|
||||
raise exception 'audit checkpoint does not identify the retained tail'
|
||||
using errcode = '23514';
|
||||
end if;
|
||||
select events.* into v_tail
|
||||
from public.audit_events as events
|
||||
where events.sequence = v_head.last_sequence;
|
||||
if not found or v_tail.event_hash is distinct from v_head.last_event_hash
|
||||
or v_tail.event_hash !~ '^[0-9a-f]{64}$' then
|
||||
raise exception 'audit retained tail is missing or does not match the checkpoint'
|
||||
using errcode = '23514';
|
||||
end if;
|
||||
if v_tail.hash_format = 'v2' then
|
||||
if v_tail.canonical_payload is null
|
||||
or pg_catalog.encode(
|
||||
pg_catalog.sha256(pg_catalog.convert_to(v_tail.canonical_payload, 'UTF8')),
|
||||
'hex'
|
||||
) <> v_tail.event_hash
|
||||
or v_tail.canonical_payload::jsonb is distinct from pg_catalog.jsonb_build_object(
|
||||
'hash_format', 'v2',
|
||||
'id', v_tail.id::text,
|
||||
'occurred_at', pg_catalog.to_char(
|
||||
v_tail.occurred_at at time zone 'UTC',
|
||||
'YYYY-MM-DD"T"HH24:MI:SS.US"Z"'
|
||||
),
|
||||
'correlation_id', v_tail.correlation_id,
|
||||
'actor_type', v_tail.actor_type,
|
||||
'actor_id', v_tail.actor_id,
|
||||
'action', v_tail.action,
|
||||
'resource_type', v_tail.resource_type,
|
||||
'resource_id', v_tail.resource_id,
|
||||
'outcome', v_tail.outcome,
|
||||
'details', v_tail.details::jsonb,
|
||||
'previous_event_hash', v_tail.previous_event_hash
|
||||
) then
|
||||
raise exception 'v2 audit retained tail payload is invalid'
|
||||
using errcode = '23514';
|
||||
end if;
|
||||
elsif v_tail.hash_format <> 'v1'
|
||||
or v_tail.sequence <> v_head.v2_start_sequence - 1 then
|
||||
raise exception 'audit retained tail hash format is invalid' using errcode = '23514';
|
||||
end if;
|
||||
if v_tail.sequence = 1 then
|
||||
if v_tail.previous_event_hash is not null then
|
||||
raise exception 'first audit event has a previous hash' using errcode = '23514';
|
||||
end if;
|
||||
else
|
||||
select events.event_hash into v_predecessor_hash
|
||||
from public.audit_events as events
|
||||
where events.sequence = v_tail.sequence - 1;
|
||||
if not found or v_tail.previous_event_hash is distinct from v_predecessor_hash then
|
||||
raise exception 'audit retained tail link is invalid' using errcode = '23514';
|
||||
end if;
|
||||
end if;
|
||||
end if;
|
||||
|
||||
v_sequence := v_head.last_sequence + 1;
|
||||
v_payload := pg_catalog.jsonb_build_object(
|
||||
'hash_format', 'v2',
|
||||
'id', p_event_id::text,
|
||||
'occurred_at', pg_catalog.to_char(
|
||||
p_occurred_at at time zone 'UTC',
|
||||
'YYYY-MM-DD"T"HH24:MI:SS.US"Z"'
|
||||
),
|
||||
'correlation_id', p_correlation_id,
|
||||
'actor_type', p_actor_type,
|
||||
'actor_id', p_actor_id,
|
||||
'action', p_action,
|
||||
'resource_type', p_resource_type,
|
||||
'resource_id', p_resource_id,
|
||||
'outcome', p_outcome,
|
||||
'details', p_details,
|
||||
'previous_event_hash', v_head.last_event_hash
|
||||
)::text;
|
||||
v_hash := pg_catalog.encode(
|
||||
pg_catalog.sha256(pg_catalog.convert_to(v_payload, 'UTF8')), 'hex'
|
||||
);
|
||||
|
||||
insert into public.audit_events (
|
||||
id, sequence, occurred_at, correlation_id, actor_type, actor_id, action,
|
||||
resource_type, resource_id, outcome, details, previous_event_hash, event_hash,
|
||||
hash_format, canonical_payload
|
||||
) values (
|
||||
p_event_id, v_sequence, p_occurred_at, p_correlation_id, p_actor_type, p_actor_id,
|
||||
p_action, p_resource_type, p_resource_id, p_outcome, p_details,
|
||||
v_head.last_event_hash, v_hash, 'v2', v_payload
|
||||
);
|
||||
update public.audit_chain_heads as head
|
||||
set event_count = v_head.event_count + 1,
|
||||
last_sequence = v_sequence,
|
||||
last_event_hash = v_hash,
|
||||
updated_at = p_occurred_at
|
||||
where head.singleton_id = 1
|
||||
and head.event_count = v_head.event_count
|
||||
and head.last_sequence = v_head.last_sequence
|
||||
and head.last_event_hash is not distinct from v_head.last_event_hash
|
||||
and head.hash_format = v_head.hash_format
|
||||
and head.v2_start_sequence = v_head.v2_start_sequence
|
||||
and head.legacy_prefix_count = v_head.legacy_prefix_count
|
||||
and head.legacy_prefix_seal = v_head.legacy_prefix_seal;
|
||||
get diagnostics v_updated = row_count;
|
||||
if v_updated <> 1 then
|
||||
raise exception 'audit checkpoint compare-and-set failed' using errcode = '40001';
|
||||
end if;
|
||||
return query select p_event_id, v_sequence, v_hash, p_occurred_at;
|
||||
end
|
||||
$append$;
|
||||
alter function modelforge_audit.append_event_v2(
|
||||
uuid, timestamptz, text, text, text, text, text, text, text, jsonb,
|
||||
bigint, bigint, text, text, bigint, bigint, text
|
||||
) owner to modelforge;
|
||||
revoke all on function modelforge_audit.append_event_v2(
|
||||
uuid, timestamptz, text, text, text, text, text, text, text, jsonb,
|
||||
bigint, bigint, text, text, bigint, bigint, text
|
||||
) from public;
|
||||
grant execute on function modelforge_audit.append_event_v2(
|
||||
uuid, timestamptz, text, text, text, text, text, text, text, jsonb,
|
||||
bigint, bigint, text, text, bigint, bigint, text
|
||||
) to modelforge_runtime;
|
||||
|
||||
grant select, insert, update, delete on all tables in schema public to modelforge_runtime;
|
||||
grant usage, select on all sequences in schema public to modelforge_runtime;
|
||||
revoke execute on all functions in schema public from public, modelforge_runtime;
|
||||
revoke insert, update, delete, truncate, references, trigger
|
||||
on public.audit_events, public.audit_chain_heads from modelforge_runtime;
|
||||
grant select on public.audit_events, public.audit_chain_heads to modelforge_runtime;
|
||||
alter default privileges for role modelforge in schema public
|
||||
grant select, insert, update, delete on tables to modelforge_runtime;
|
||||
alter default privileges for role modelforge in schema public
|
||||
grant usage, select on sequences to modelforge_runtime;
|
||||
alter default privileges for role modelforge in schema public
|
||||
revoke execute on functions from public;
|
||||
|
||||
do $database_privileges$
|
||||
begin
|
||||
execute pg_catalog.format(
|
||||
'revoke create, temporary on database %I from modelforge_runtime',
|
||||
pg_catalog.current_database()
|
||||
);
|
||||
end
|
||||
$database_privileges$;
|
||||
"""
|
||||
|
||||
|
||||
def _normalise_timestamp(value: datetime | str) -> str:
|
||||
if isinstance(value, datetime):
|
||||
moment = value
|
||||
elif isinstance(value, str):
|
||||
candidate = value.strip()
|
||||
if candidate.endswith("Z"):
|
||||
candidate = candidate[:-1] + "+00:00"
|
||||
try:
|
||||
moment = datetime.fromisoformat(candidate)
|
||||
except ValueError as error:
|
||||
raise RuntimeError("legacy audit event has an invalid occurred_at") from error
|
||||
else:
|
||||
raise RuntimeError("legacy audit event has an invalid occurred_at")
|
||||
if moment.tzinfo is None:
|
||||
moment = moment.replace(tzinfo=UTC)
|
||||
return moment.astimezone(UTC).isoformat(timespec="microseconds").replace("+00:00", "Z")
|
||||
|
||||
|
||||
def _details(value: Any) -> dict[str, Any]:
|
||||
if isinstance(value, str):
|
||||
try:
|
||||
value = json.loads(value)
|
||||
except json.JSONDecodeError as error:
|
||||
raise RuntimeError("legacy audit event details are not valid JSON") from error
|
||||
if not isinstance(value, dict):
|
||||
raise RuntimeError("legacy audit event details must be a JSON object")
|
||||
return value
|
||||
|
||||
|
||||
def _legacy_hash(row: sa.RowMapping) -> str:
|
||||
payload = {
|
||||
"correlation_id": row["correlation_id"],
|
||||
"actor_type": row["actor_type"],
|
||||
"actor_id": row["actor_id"],
|
||||
"action": row["action"],
|
||||
"resource_type": row["resource_type"],
|
||||
"resource_id": row["resource_id"],
|
||||
"outcome": row["outcome"],
|
||||
"details": _details(row["details"]),
|
||||
"previous_event_hash": row["previous_event_hash"],
|
||||
}
|
||||
encoded = json.dumps(payload, sort_keys=True, separators=(",", ":"))
|
||||
return hashlib.sha256(encoded.encode("utf-8")).hexdigest()
|
||||
|
||||
|
||||
def _prefix_entry(row: sa.RowMapping) -> bytes:
|
||||
try:
|
||||
event_id = str(uuid.UUID(str(row["id"])))
|
||||
except (AttributeError, TypeError, ValueError) as error:
|
||||
raise RuntimeError("legacy audit event id is not a UUID") from error
|
||||
payload = {
|
||||
"sequence": int(row["sequence"]),
|
||||
"id": event_id,
|
||||
"occurred_at": _normalise_timestamp(row["occurred_at"]),
|
||||
"event_hash": str(row["event_hash"]),
|
||||
}
|
||||
return json.dumps(payload, sort_keys=True, separators=(",", ":")).encode("utf-8") + b"\n"
|
||||
|
||||
|
||||
def _validate_legacy_rows(rows: list[sa.RowMapping]) -> dict[str, Any]:
|
||||
"""Validate the production-shaped v1 chain and return its immutable cutover state."""
|
||||
|
||||
previous_hash: str | None = None
|
||||
prefix = hashlib.sha256()
|
||||
prefix.update(_LEGACY_PREFIX_DOMAIN)
|
||||
for expected_sequence, row in enumerate(rows, start=1):
|
||||
try:
|
||||
sequence = int(row["sequence"])
|
||||
except (TypeError, ValueError) as error:
|
||||
raise RuntimeError("legacy audit event sequence is not an integer") from error
|
||||
if sequence != expected_sequence:
|
||||
raise RuntimeError(
|
||||
f"legacy audit chain has sequence {sequence}; expected {expected_sequence}"
|
||||
)
|
||||
event_hash = str(row["event_hash"])
|
||||
if _SHA256.fullmatch(event_hash) is None:
|
||||
raise RuntimeError(f"legacy audit event {sequence} has a malformed event hash")
|
||||
if row["previous_event_hash"] != previous_hash:
|
||||
raise RuntimeError(f"legacy audit event {sequence} has an invalid previous hash")
|
||||
if _legacy_hash(row) != event_hash:
|
||||
raise RuntimeError(f"legacy audit event {sequence} content hash is invalid")
|
||||
prefix.update(_prefix_entry(row))
|
||||
previous_hash = event_hash
|
||||
count = len(rows)
|
||||
return {
|
||||
"event_count": count,
|
||||
"last_sequence": count,
|
||||
"last_event_hash": previous_hash,
|
||||
"v2_start_sequence": count + 1,
|
||||
"legacy_prefix_count": count,
|
||||
"legacy_prefix_seal": prefix.hexdigest(),
|
||||
}
|
||||
|
||||
|
||||
def _read_and_validate_legacy_chain(connection: Connection) -> dict[str, Any]:
|
||||
rows = list(
|
||||
connection.execute(
|
||||
sa.text(
|
||||
"select id, sequence, occurred_at, correlation_id, actor_type, actor_id, "
|
||||
"action, resource_type, resource_id, outcome, details, previous_event_hash, "
|
||||
"event_hash from audit_events order by sequence, id"
|
||||
)
|
||||
).mappings()
|
||||
)
|
||||
return _validate_legacy_rows(rows)
|
||||
|
||||
|
||||
def _lock_legacy_audit_chain(connection: Connection) -> None:
|
||||
"""Serialize validation and checkpoint seed against every legacy writer.
|
||||
|
||||
The shared advisory key coordinates with 0024-aware writers. ``ACCESS EXCLUSIVE`` also blocks
|
||||
pre-0024 applications, which do not know that key, until this migration transaction commits.
|
||||
SQLite is test-only; a no-op write upgrades its deferred transaction to a writer before the
|
||||
validation read so another test connection cannot append into the validation/seed window.
|
||||
"""
|
||||
|
||||
dialect = connection.dialect.name
|
||||
if dialect == "postgresql":
|
||||
connection.execute(
|
||||
sa.text("select pg_advisory_xact_lock(:lock_key)"),
|
||||
{"lock_key": _AUDIT_CHAIN_LOCK_KEY},
|
||||
)
|
||||
connection.execute(sa.text("lock table audit_events in access exclusive mode"))
|
||||
return
|
||||
if dialect == "sqlite":
|
||||
connection.execute(sa.text("update audit_events set event_hash = event_hash where 1 = 0"))
|
||||
return
|
||||
raise RuntimeError(f"audit-chain migration does not support the {dialect!r} dialect")
|
||||
|
||||
|
||||
def _validate_postgres_role_preflight(connection: Connection) -> None:
|
||||
"""Require the separately provisioned non-superuser owner/runtime roles before DDL.
|
||||
|
||||
Existing 1.2.1 installations commonly made ``modelforge`` the cluster bootstrap superuser.
|
||||
That credential cannot be converted into the API boundary implicitly by an application
|
||||
migration. Operators must first run the documented admin-owned provisioning step; failure is
|
||||
deliberately before this migration changes a column or seeds a checkpoint.
|
||||
"""
|
||||
|
||||
if connection.dialect.name != "postgresql":
|
||||
return
|
||||
roles = list(
|
||||
connection.execute(
|
||||
sa.text(
|
||||
"select rolname, rolsuper, rolinherit, rolcreaterole, rolcreatedb, "
|
||||
"rolcanlogin, rolreplication, rolbypassrls from pg_catalog.pg_roles "
|
||||
"where rolname in (:owner_role, :runtime_role) order by rolname"
|
||||
),
|
||||
{"owner_role": _AUDIT_OWNER_ROLE, "runtime_role": _AUDIT_RUNTIME_ROLE},
|
||||
).mappings()
|
||||
)
|
||||
by_name = {str(row["rolname"]): row for row in roles}
|
||||
if set(by_name) != {_AUDIT_OWNER_ROLE, _AUDIT_RUNTIME_ROLE}:
|
||||
raise RuntimeError(
|
||||
"audit migration preflight requires separately provisioned modelforge owner and "
|
||||
"modelforge_runtime roles; run the v1.2.1-to-schema-0024 role provisioning step"
|
||||
)
|
||||
current_role = str(connection.scalar(sa.text("select current_user")))
|
||||
if current_role != _AUDIT_OWNER_ROLE:
|
||||
raise RuntimeError(
|
||||
"audit migration must run with the non-superuser modelforge owner credential"
|
||||
)
|
||||
session_role = str(connection.scalar(sa.text("select session_user")))
|
||||
if session_role != _AUDIT_OWNER_ROLE:
|
||||
raise RuntimeError(
|
||||
"audit migration must authenticate directly as modelforge, not SET ROLE from admin"
|
||||
)
|
||||
for role_name, require_noinherit in (
|
||||
(_AUDIT_OWNER_ROLE, False),
|
||||
(_AUDIT_RUNTIME_ROLE, True),
|
||||
):
|
||||
role = by_name[role_name]
|
||||
forbidden = any(
|
||||
bool(role[field])
|
||||
for field in (
|
||||
"rolsuper",
|
||||
"rolcreaterole",
|
||||
"rolcreatedb",
|
||||
"rolreplication",
|
||||
"rolbypassrls",
|
||||
)
|
||||
)
|
||||
if forbidden or not bool(role["rolcanlogin"]):
|
||||
raise RuntimeError(f"database role {role_name} has forbidden administrative powers")
|
||||
if require_noinherit and bool(role["rolinherit"]):
|
||||
raise RuntimeError("modelforge_runtime must be provisioned NOINHERIT")
|
||||
app_role_membership_count = int(
|
||||
connection.scalar(
|
||||
sa.text(
|
||||
"select count(*) from pg_catalog.pg_auth_members as membership "
|
||||
"join pg_catalog.pg_roles as member on member.oid = membership.member "
|
||||
"where member.rolname in (:runtime_role, :owner_role)"
|
||||
),
|
||||
{"runtime_role": _AUDIT_RUNTIME_ROLE, "owner_role": _AUDIT_OWNER_ROLE},
|
||||
)
|
||||
or 0
|
||||
)
|
||||
if app_role_membership_count:
|
||||
raise RuntimeError(
|
||||
"modelforge and modelforge_runtime must have no SET ROLE-capable memberships"
|
||||
)
|
||||
|
||||
|
||||
def _install_postgres_audit_boundary(connection: Connection) -> None:
|
||||
if connection.dialect.name == "postgresql":
|
||||
for statement in _postgres_sql_statements(_POSTGRES_AUDIT_BOUNDARY_SQL):
|
||||
_exec_postgres_sql(connection, statement)
|
||||
|
||||
|
||||
def _exec_postgres_sql(connection: Connection, statement: str) -> None:
|
||||
"""Execute trusted static SQL without exposing PostgreSQL percent syntax to DBAPI parsing."""
|
||||
|
||||
paramstyle = getattr(connection.dialect, "paramstyle", None)
|
||||
driver_statement = (
|
||||
statement.replace("%", "%%")
|
||||
if paramstyle in {"format", "pyformat"}
|
||||
else statement
|
||||
)
|
||||
connection.exec_driver_sql(driver_statement)
|
||||
|
||||
|
||||
def _postgres_sql_statements(script: str) -> list[str]:
|
||||
"""Split this migration's trusted static SQL without splitting function bodies."""
|
||||
|
||||
statements: list[str] = []
|
||||
start = 0
|
||||
index = 0
|
||||
quote: str | None = None
|
||||
while index < len(script):
|
||||
if quote is not None:
|
||||
if quote == "'" and script.startswith("''", index):
|
||||
index += 2
|
||||
continue
|
||||
if script.startswith(quote, index):
|
||||
index += len(quote)
|
||||
quote = None
|
||||
continue
|
||||
index += 1
|
||||
continue
|
||||
character = script[index]
|
||||
if character == "'":
|
||||
quote = "'"
|
||||
index += 1
|
||||
continue
|
||||
if character == "$":
|
||||
delimiter = re.match(r"\$[A-Za-z_][A-Za-z0-9_]*\$|\$\$", script[index:])
|
||||
if delimiter is not None:
|
||||
quote = delimiter.group(0)
|
||||
index += len(quote)
|
||||
continue
|
||||
if character == ";":
|
||||
statement = script[start:index].strip()
|
||||
if statement:
|
||||
statements.append(statement)
|
||||
start = index + 1
|
||||
index += 1
|
||||
trailing = script[start:].strip()
|
||||
if quote is not None:
|
||||
raise RuntimeError("generated PostgreSQL audit boundary SQL has an unterminated literal")
|
||||
if trailing:
|
||||
statements.append(trailing)
|
||||
return statements
|
||||
|
||||
|
||||
def _remove_postgres_audit_boundary(connection: Connection) -> None:
|
||||
if connection.dialect.name != "postgresql":
|
||||
return
|
||||
script = (
|
||||
"drop trigger if exists trg_modelforge_audit_events_owner on public.audit_events; "
|
||||
"drop trigger if exists trg_modelforge_audit_events_truncate_owner "
|
||||
"on public.audit_events; "
|
||||
"drop trigger if exists trg_modelforge_audit_head_owner on public.audit_chain_heads; "
|
||||
"drop trigger if exists trg_modelforge_audit_head_truncate_owner "
|
||||
"on public.audit_chain_heads; "
|
||||
"drop function if exists modelforge_audit.append_event_v2("
|
||||
"uuid, timestamptz, text, text, text, text, text, text, text, jsonb, "
|
||||
"bigint, bigint, text, text, bigint, bigint, text); "
|
||||
"drop function if exists modelforge_audit.enforce_owner_mutation(); "
|
||||
"drop schema if exists modelforge_audit; "
|
||||
"grant select, insert on public.audit_events to modelforge_runtime"
|
||||
)
|
||||
for statement in _postgres_sql_statements(script):
|
||||
_exec_postgres_sql(connection, statement)
|
||||
|
||||
|
||||
def upgrade() -> None:
|
||||
connection = op.get_bind()
|
||||
_validate_postgres_role_preflight(connection)
|
||||
_lock_legacy_audit_chain(connection)
|
||||
# Validation deliberately precedes every schema mutation. In particular, legacy recovery
|
||||
# markers written with a random hash stop the migration instead of being blessed by a seal.
|
||||
legacy = _read_and_validate_legacy_chain(connection)
|
||||
|
||||
op.add_column(
|
||||
"audit_events",
|
||||
sa.Column("hash_format", sa.String(length=16), nullable=True),
|
||||
)
|
||||
op.add_column(
|
||||
"audit_events",
|
||||
sa.Column("canonical_payload", sa.Text(), nullable=True),
|
||||
)
|
||||
connection.execute(sa.text("update audit_events set hash_format = 'v1'"))
|
||||
with op.batch_alter_table("audit_events") as batch:
|
||||
batch.alter_column(
|
||||
"hash_format",
|
||||
existing_type=sa.String(length=16),
|
||||
nullable=False,
|
||||
)
|
||||
batch.create_check_constraint(
|
||||
"ck_audit_event_hash_format", "hash_format IN ('v1', 'v2')"
|
||||
)
|
||||
batch.create_check_constraint(
|
||||
"ck_audit_event_canonical_payload",
|
||||
"((hash_format = 'v1' AND canonical_payload IS NULL) OR "
|
||||
"(hash_format = 'v2' AND canonical_payload IS NOT NULL))",
|
||||
)
|
||||
|
||||
op.create_table(
|
||||
"audit_chain_heads",
|
||||
sa.Column("singleton_id", sa.Integer(), nullable=False),
|
||||
sa.Column("event_count", sa.BigInteger(), nullable=False),
|
||||
sa.Column("last_sequence", sa.BigInteger(), nullable=False),
|
||||
sa.Column("last_event_hash", sa.String(length=64), nullable=True),
|
||||
sa.Column("hash_format", sa.String(length=16), nullable=False),
|
||||
sa.Column("v2_start_sequence", sa.BigInteger(), nullable=False),
|
||||
sa.Column("legacy_prefix_count", sa.BigInteger(), nullable=False),
|
||||
sa.Column("legacy_prefix_seal", sa.String(length=64), nullable=False),
|
||||
sa.Column(
|
||||
"updated_at",
|
||||
sa.DateTime(timezone=True),
|
||||
server_default=sa.text("CURRENT_TIMESTAMP"),
|
||||
nullable=False,
|
||||
),
|
||||
sa.CheckConstraint("singleton_id = 1", name="ck_audit_chain_head_singleton"),
|
||||
sa.CheckConstraint("event_count >= 0", name="ck_audit_chain_head_count"),
|
||||
sa.CheckConstraint("last_sequence >= 0", name="ck_audit_chain_head_sequence"),
|
||||
sa.CheckConstraint(
|
||||
"event_count = last_sequence",
|
||||
name="ck_audit_chain_head_count_sequence",
|
||||
),
|
||||
sa.CheckConstraint("v2_start_sequence >= 1", name="ck_audit_chain_head_cutover"),
|
||||
sa.CheckConstraint(
|
||||
"legacy_prefix_count = v2_start_sequence - 1",
|
||||
name="ck_audit_chain_head_prefix_count",
|
||||
),
|
||||
sa.CheckConstraint(
|
||||
"legacy_prefix_count <= event_count",
|
||||
name="ck_audit_chain_head_prefix_within_chain",
|
||||
),
|
||||
sa.CheckConstraint("hash_format = 'v2'", name="ck_audit_chain_head_hash_format"),
|
||||
sa.CheckConstraint(
|
||||
"length(legacy_prefix_seal) = 64",
|
||||
name="ck_audit_chain_head_prefix_seal",
|
||||
),
|
||||
sa.CheckConstraint(
|
||||
"((event_count = 0 AND last_sequence = 0 AND last_event_hash IS NULL) OR "
|
||||
"(event_count > 0 AND last_sequence > 0 AND last_event_hash IS NOT NULL))",
|
||||
name="ck_audit_chain_head_shape",
|
||||
),
|
||||
sa.PrimaryKeyConstraint("singleton_id"),
|
||||
)
|
||||
connection.execute(
|
||||
sa.text(
|
||||
"insert into audit_chain_heads (singleton_id, event_count, last_sequence, "
|
||||
"last_event_hash, hash_format, v2_start_sequence, legacy_prefix_count, "
|
||||
"legacy_prefix_seal) values (1, :event_count, :last_sequence, :last_event_hash, "
|
||||
"'v2', :v2_start_sequence, :legacy_prefix_count, :legacy_prefix_seal)"
|
||||
),
|
||||
legacy,
|
||||
)
|
||||
_install_postgres_audit_boundary(connection)
|
||||
|
||||
|
||||
def downgrade() -> None:
|
||||
connection = op.get_bind()
|
||||
_validate_postgres_role_preflight(connection)
|
||||
_lock_legacy_audit_chain(connection)
|
||||
non_legacy = int(
|
||||
connection.scalar(
|
||||
sa.text("select count(*) from audit_events where hash_format <> 'v1'")
|
||||
)
|
||||
or 0
|
||||
)
|
||||
if non_legacy:
|
||||
raise RuntimeError(
|
||||
"cannot downgrade audit hash format after v2 events exist without rewriting history"
|
||||
)
|
||||
legacy = _read_and_validate_legacy_chain(connection)
|
||||
head = connection.execute(
|
||||
sa.text(
|
||||
"select event_count, last_sequence, last_event_hash, hash_format, "
|
||||
"v2_start_sequence, legacy_prefix_count, legacy_prefix_seal "
|
||||
"from audit_chain_heads where singleton_id = 1"
|
||||
)
|
||||
).mappings().one_or_none()
|
||||
if head is None or head["hash_format"] != "v2":
|
||||
raise RuntimeError("cannot downgrade a missing or malformed audit checkpoint")
|
||||
for key, expected in legacy.items():
|
||||
if head[key] != expected:
|
||||
raise RuntimeError(f"cannot downgrade: audit checkpoint {key} is inconsistent")
|
||||
|
||||
_remove_postgres_audit_boundary(connection)
|
||||
op.drop_table("audit_chain_heads")
|
||||
with op.batch_alter_table("audit_events") as batch:
|
||||
batch.drop_constraint("ck_audit_event_canonical_payload", type_="check")
|
||||
batch.drop_constraint("ck_audit_event_hash_format", type_="check")
|
||||
batch.drop_column("canonical_payload")
|
||||
batch.drop_column("hash_format")
|
||||
Reference in New Issue
Block a user