183 lines
8.8 KiB
Python
183 lines
8.8 KiB
Python
# #region Alembic.ScenarioRegistryTables [C:3] [TYPE Module] [SEMANTICS alembic,scenario,registry,revision,lifecycle,staleness]
|
|
# @ingroup Alembic
|
|
# @BRIEF Add 042 scenario registry tables: entries, revisions, staleness signals, lifecycle audit.
|
|
# @LAYER Database
|
|
"""add scenario registry tables (042 dashboard-scenario-registry)
|
|
|
|
Revision ID: v1w2x3y4z5a6
|
|
Revises: u1v2w3x4y5z6
|
|
Create Date: 2026-08-19
|
|
|
|
"""
|
|
from collections.abc import Sequence
|
|
|
|
import sqlalchemy as sa
|
|
|
|
from alembic import op
|
|
|
|
# revision identifiers, used by Alembic.
|
|
revision: str = "v1w2x3y4z5a6"
|
|
down_revision: str | Sequence[str] | None = "u1v2w3x4y5z6"
|
|
branch_labels: str | Sequence[str] | None = None
|
|
depends_on: str | Sequence[str] | None = None
|
|
|
|
|
|
def upgrade() -> None:
|
|
"""Create scenario_registry_entries, scenario_revisions, scenario_staleness_signals, scenario_lifecycle_audit.
|
|
|
|
entry.current_revision_id is a plain indexed pointer (no FK): a circular FK
|
|
(revisions.scenario_id -> entries) would break sequential inserts under SQLite
|
|
FK enforcement and is unnecessary — revisions are append-only and pointer
|
|
integrity is owned by the activation service.
|
|
"""
|
|
# ── scenario_registry_entries ──
|
|
op.create_table(
|
|
"scenario_registry_entries",
|
|
sa.Column("scenario_id", sa.String(length=36), nullable=False),
|
|
sa.Column("scenario_key", sa.String(length=255), nullable=False),
|
|
sa.Column("name", sa.String(length=255), nullable=False),
|
|
sa.Column("description", sa.Text(), nullable=True),
|
|
sa.Column("dashboard_id", sa.Integer(), nullable=False),
|
|
sa.Column("environment_ids", sa.JSON(), nullable=False),
|
|
sa.Column("owner_id", sa.String(length=128), nullable=False),
|
|
sa.Column("owner_username", sa.String(length=128), nullable=False),
|
|
sa.Column("tags", sa.JSON(), nullable=False),
|
|
sa.Column("metadata_version", sa.String(length=64), nullable=False),
|
|
sa.Column("current_revision_id", sa.String(length=36), nullable=True),
|
|
sa.Column("lifecycle_status", sa.String(length=32), nullable=False),
|
|
sa.Column("validation_status", sa.String(length=32), nullable=False),
|
|
sa.Column("last_run_id", sa.String(length=36), nullable=True),
|
|
sa.Column("last_successful_run_id", sa.String(length=36), nullable=True),
|
|
sa.Column("health", sa.String(length=16), nullable=False),
|
|
sa.Column("baseline_compatibility", sa.String(length=64), nullable=True),
|
|
sa.Column("source_scenario_id", sa.String(length=36), nullable=True),
|
|
sa.Column("source_revision_id", sa.String(length=36), nullable=True),
|
|
sa.Column("last_modified_at", sa.DateTime(), nullable=False),
|
|
sa.Column("created_at", sa.DateTime(), nullable=False),
|
|
sa.PrimaryKeyConstraint("scenario_id"),
|
|
)
|
|
op.create_index("ix_scenario_registry_entries_key", "scenario_registry_entries", ["scenario_key"])
|
|
op.create_index(
|
|
"ix_scenario_registry_entries_dashboard", "scenario_registry_entries", ["dashboard_id"]
|
|
)
|
|
op.create_index(
|
|
"ix_scenario_registry_entries_owner", "scenario_registry_entries", ["owner_username"]
|
|
)
|
|
op.create_index(
|
|
"ix_scenario_registry_entries_lifecycle", "scenario_registry_entries", ["lifecycle_status"]
|
|
)
|
|
op.create_index(
|
|
"ix_scenario_registry_entries_current_revision",
|
|
"scenario_registry_entries",
|
|
["current_revision_id"],
|
|
)
|
|
|
|
# ── scenario_revisions ──
|
|
op.create_table(
|
|
"scenario_revisions",
|
|
sa.Column("revision_id", sa.String(length=36), nullable=False),
|
|
sa.Column("scenario_id", sa.String(length=36), nullable=False),
|
|
sa.Column("content_hash", sa.String(length=64), nullable=False),
|
|
sa.Column("parent_revision_id", sa.String(length=36), nullable=True),
|
|
sa.Column("graph_snapshot", sa.JSON(), nullable=False),
|
|
sa.Column("execution_template_hash", sa.String(length=64), nullable=False),
|
|
sa.Column("template_version", sa.String(length=32), nullable=False),
|
|
sa.Column("schema_version", sa.Integer(), nullable=False),
|
|
sa.Column("compatibility_family", sa.String(length=64), nullable=False),
|
|
sa.Column("change_summary", sa.JSON(), nullable=False),
|
|
sa.Column("created_by", sa.String(length=128), nullable=False),
|
|
sa.Column("created_at", sa.DateTime(), nullable=False),
|
|
sa.Column("activation_status", sa.String(length=16), nullable=False),
|
|
sa.Column("activated_by", sa.String(length=128), nullable=True),
|
|
sa.Column("activated_at", sa.DateTime(), nullable=True),
|
|
sa.Column("activation_agent_action_id", sa.String(length=128), nullable=True),
|
|
sa.ForeignKeyConstraint(["scenario_id"], ["scenario_registry_entries.scenario_id"], ondelete="CASCADE"),
|
|
sa.ForeignKeyConstraint(["parent_revision_id"], ["scenario_revisions.revision_id"], ondelete="SET NULL"),
|
|
sa.PrimaryKeyConstraint("revision_id"),
|
|
)
|
|
op.create_index("ix_scenario_revisions_scenario", "scenario_revisions", ["scenario_id"])
|
|
op.create_index(
|
|
"ix_scenario_revisions_activation", "scenario_revisions", ["activation_status"]
|
|
)
|
|
|
|
# ── entry.current_revision_id is a plain indexed pointer (no FK) — see upgrade() rationale ──
|
|
|
|
# ── scenario_staleness_signals ──
|
|
op.create_table(
|
|
"scenario_staleness_signals",
|
|
sa.Column("id", sa.String(length=36), nullable=False),
|
|
sa.Column("scenario_id", sa.String(length=36), nullable=False),
|
|
sa.Column("source_type", sa.String(length=64), nullable=False),
|
|
sa.Column("source_fingerprint", sa.String(length=128), nullable=False),
|
|
sa.Column("kind", sa.String(length=32), nullable=False),
|
|
sa.Column("severity", sa.String(length=16), nullable=False),
|
|
sa.Column("reason", sa.Text(), nullable=False),
|
|
sa.Column("affected_ref", sa.String(length=255), nullable=False),
|
|
sa.Column("detected_at", sa.DateTime(), nullable=False),
|
|
sa.Column("resolved_at", sa.DateTime(), nullable=True),
|
|
sa.ForeignKeyConstraint(["scenario_id"], ["scenario_registry_entries.scenario_id"], ondelete="CASCADE"),
|
|
sa.PrimaryKeyConstraint("id"),
|
|
sa.UniqueConstraint(
|
|
"scenario_id",
|
|
"source_type",
|
|
"source_fingerprint",
|
|
"affected_ref",
|
|
name="uq_scenario_staleness_signal_identity",
|
|
),
|
|
)
|
|
op.create_index(
|
|
"ix_scenario_staleness_signals_scenario", "scenario_staleness_signals", ["scenario_id"]
|
|
)
|
|
|
|
# ── scenario_lifecycle_audit ──
|
|
op.create_table(
|
|
"scenario_lifecycle_audit",
|
|
sa.Column("id", sa.String(length=36), nullable=False),
|
|
sa.Column("scenario_id", sa.String(length=36), nullable=False),
|
|
sa.Column("from_state", sa.String(length=32), nullable=True),
|
|
sa.Column("to_state", sa.String(length=32), nullable=False),
|
|
sa.Column("actor_id", sa.String(length=128), nullable=False),
|
|
sa.Column("actor_type", sa.String(length=16), nullable=False),
|
|
sa.Column("reason", sa.Text(), nullable=True),
|
|
sa.Column("agent_action_id", sa.String(length=128), nullable=True),
|
|
sa.Column("created_at", sa.DateTime(), nullable=False),
|
|
sa.ForeignKeyConstraint(["scenario_id"], ["scenario_registry_entries.scenario_id"], ondelete="CASCADE"),
|
|
sa.PrimaryKeyConstraint("id"),
|
|
)
|
|
op.create_index(
|
|
"ix_scenario_lifecycle_audit_scenario", "scenario_lifecycle_audit", ["scenario_id"]
|
|
)
|
|
op.create_index(
|
|
"ix_scenario_lifecycle_audit_created", "scenario_lifecycle_audit", ["created_at"]
|
|
)
|
|
|
|
|
|
def downgrade() -> None:
|
|
"""Drop the scenario registry tables (reverse order of FK dependencies)."""
|
|
op.drop_index("ix_scenario_lifecycle_audit_created", table_name="scenario_lifecycle_audit")
|
|
op.drop_index("ix_scenario_lifecycle_audit_scenario", table_name="scenario_lifecycle_audit")
|
|
op.drop_table("scenario_lifecycle_audit")
|
|
|
|
op.drop_index(
|
|
"ix_scenario_staleness_signals_scenario", table_name="scenario_staleness_signals"
|
|
)
|
|
op.drop_table("scenario_staleness_signals")
|
|
|
|
op.drop_index(
|
|
"ix_scenario_registry_entries_current_revision",
|
|
table_name="scenario_registry_entries",
|
|
)
|
|
|
|
op.drop_index("ix_scenario_revisions_activation", table_name="scenario_revisions")
|
|
op.drop_index("ix_scenario_revisions_scenario", table_name="scenario_revisions")
|
|
op.drop_table("scenario_revisions")
|
|
|
|
op.drop_index("ix_scenario_registry_entries_lifecycle", table_name="scenario_registry_entries")
|
|
op.drop_index("ix_scenario_registry_entries_owner", table_name="scenario_registry_entries")
|
|
op.drop_index(
|
|
"ix_scenario_registry_entries_dashboard", table_name="scenario_registry_entries"
|
|
)
|
|
op.drop_index("ix_scenario_registry_entries_key", table_name="scenario_registry_entries")
|
|
op.drop_table("scenario_registry_entries")
|
|
# #endregion Alembic.ScenarioRegistryTables
|