Tests / Test passed: 819
Adds cloud/skills.py (domain + service), SQLAlchemy models and Alembic migration 0010_skill_management (cloud_skills, cloud_skill_entitlements, cloud_skill_sync_state, a per-host changelog, and host_skill_inventory), and repository methods with a monotonic per-host entitlement_version that drives correct incremental fetch_host_delta. cloud-api suite green (41 passed); HEAD_REVISION bumped to 0010. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
109 lines
3.6 KiB
Python
109 lines
3.6 KiB
Python
"""Add Cloud-managed Skills, per-host entitlement, and sync versioning."""
|
|
|
|
from __future__ import annotations
|
|
|
|
from alembic import op
|
|
import sqlalchemy as sa
|
|
|
|
|
|
revision = "0010_skill_management"
|
|
down_revision = "0009_planner_decision_log"
|
|
branch_labels = None
|
|
depends_on = None
|
|
|
|
|
|
def upgrade() -> None:
|
|
op.create_table(
|
|
"cloud_skills",
|
|
sa.Column("id", sa.String(), primary_key=True),
|
|
sa.Column("name", sa.String(), nullable=False),
|
|
sa.Column("name_normalized", sa.String(), nullable=False),
|
|
sa.Column("kind", sa.String(), nullable=False),
|
|
sa.Column("description", sa.Text(), nullable=False),
|
|
sa.Column("tags_json", sa.Text(), nullable=False),
|
|
sa.Column("content", sa.Text(), nullable=False),
|
|
sa.Column("steps_json", sa.Text(), nullable=False),
|
|
sa.Column("parameters_json", sa.Text(), nullable=False),
|
|
sa.Column("revision", sa.Integer(), nullable=False, server_default="1"),
|
|
sa.Column("created_at", sa.String(), nullable=False),
|
|
sa.Column("updated_at", sa.String(), nullable=False),
|
|
sa.UniqueConstraint(
|
|
"name_normalized", name="uq_cloud_skills_name_normalized"
|
|
),
|
|
)
|
|
op.create_index("ix_cloud_skills_kind", "cloud_skills", ["kind"])
|
|
|
|
op.create_table(
|
|
"cloud_skill_entitlements",
|
|
sa.Column(
|
|
"skill_id",
|
|
sa.String(),
|
|
sa.ForeignKey("cloud_skills.id"),
|
|
primary_key=True,
|
|
),
|
|
sa.Column("host_id", sa.String(), primary_key=True),
|
|
sa.Column("granted_at", sa.String(), nullable=False),
|
|
sa.UniqueConstraint(
|
|
"skill_id",
|
|
"host_id",
|
|
name="uq_cloud_skill_entitlements_skill_host",
|
|
),
|
|
)
|
|
op.create_index(
|
|
"ix_cloud_skill_entitlements_host_id", "cloud_skill_entitlements", ["host_id"]
|
|
)
|
|
op.create_index(
|
|
"ix_cloud_skill_entitlements_skill_id",
|
|
"cloud_skill_entitlements",
|
|
["skill_id"],
|
|
)
|
|
|
|
op.create_table(
|
|
"cloud_skill_sync_state",
|
|
sa.Column("host_id", sa.String(), primary_key=True),
|
|
sa.Column("last_version", sa.Integer(), nullable=False, server_default="0"),
|
|
sa.Column("updated_at", sa.String(), nullable=False),
|
|
)
|
|
|
|
op.create_table(
|
|
"cloud_skill_host_changes",
|
|
sa.Column("host_id", sa.String(), primary_key=True),
|
|
sa.Column("version", sa.Integer(), primary_key=True),
|
|
sa.Column("skill_id", sa.String(), nullable=False),
|
|
sa.Column("change_type", sa.String(), nullable=False),
|
|
sa.Column("recorded_at", sa.String(), nullable=False),
|
|
)
|
|
op.create_index(
|
|
"ix_cloud_skill_host_changes_host_version",
|
|
"cloud_skill_host_changes",
|
|
["host_id", "version"],
|
|
)
|
|
|
|
op.create_table(
|
|
"host_skill_inventory",
|
|
sa.Column("host_id", sa.String(), primary_key=True),
|
|
sa.Column("payload_json", sa.Text(), nullable=False),
|
|
sa.Column("reported_at", sa.String(), nullable=False),
|
|
)
|
|
|
|
|
|
def downgrade() -> None:
|
|
op.drop_table("host_skill_inventory")
|
|
op.drop_index(
|
|
"ix_cloud_skill_host_changes_host_version",
|
|
table_name="cloud_skill_host_changes",
|
|
)
|
|
op.drop_table("cloud_skill_host_changes")
|
|
op.drop_table("cloud_skill_sync_state")
|
|
op.drop_index(
|
|
"ix_cloud_skill_entitlements_skill_id",
|
|
table_name="cloud_skill_entitlements",
|
|
)
|
|
op.drop_index(
|
|
"ix_cloud_skill_entitlements_host_id",
|
|
table_name="cloud_skill_entitlements",
|
|
)
|
|
op.drop_table("cloud_skill_entitlements")
|
|
op.drop_index("ix_cloud_skills_kind", table_name="cloud_skills")
|
|
op.drop_table("cloud_skills")
|