Files
q792602257andClaude Opus 4.6 52e442790a
Tests / Test passed: 819
feat(cloud): cloud-managed skill store + per-host entitlement + sync versioning
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>
2026-07-15 07:45:22 +08:00

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")