feat(rulebook): migration 0055 — rulebooks, topics, rules, subscriptions
This commit is contained in:
@@ -0,0 +1,153 @@
|
||||
"""rulebook tables: rulebooks, rulebook_topics, rules, project_rulebook_subscriptions
|
||||
|
||||
Revision ID: 0055
|
||||
Revises: 0054
|
||||
Create Date: 2026-05-27
|
||||
|
||||
Adds the Scribe Rulebook hierarchy as a fourth top-level entity, sibling
|
||||
to Project. Rules carry a structural (statement, why, how_to_apply)
|
||||
triple. Projects subscribe to Rulebooks (many-to-many). See the design
|
||||
doc at docs/superpowers/specs/2026-05-27-scribe-rulebook-design.md.
|
||||
"""
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
|
||||
|
||||
revision = "0055"
|
||||
down_revision = "0054"
|
||||
branch_labels = None
|
||||
depends_on = None
|
||||
|
||||
|
||||
def upgrade() -> None:
|
||||
op.create_table(
|
||||
"rulebooks",
|
||||
sa.Column("id", sa.BigInteger, primary_key=True),
|
||||
sa.Column(
|
||||
"owner_user_id",
|
||||
sa.BigInteger,
|
||||
sa.ForeignKey("users.id", ondelete="CASCADE"),
|
||||
nullable=False,
|
||||
),
|
||||
sa.Column("title", sa.Text, nullable=False),
|
||||
sa.Column("description", sa.Text),
|
||||
sa.Column(
|
||||
"created_at",
|
||||
sa.DateTime(timezone=True),
|
||||
nullable=False,
|
||||
server_default=sa.text("now()"),
|
||||
),
|
||||
sa.Column(
|
||||
"updated_at",
|
||||
sa.DateTime(timezone=True),
|
||||
nullable=False,
|
||||
server_default=sa.text("now()"),
|
||||
),
|
||||
)
|
||||
op.create_index("ix_rulebooks_owner", "rulebooks", ["owner_user_id"])
|
||||
|
||||
op.create_table(
|
||||
"rulebook_topics",
|
||||
sa.Column("id", sa.BigInteger, primary_key=True),
|
||||
sa.Column(
|
||||
"rulebook_id",
|
||||
sa.BigInteger,
|
||||
sa.ForeignKey("rulebooks.id", ondelete="CASCADE"),
|
||||
nullable=False,
|
||||
),
|
||||
sa.Column("title", sa.Text, nullable=False),
|
||||
sa.Column("description", sa.Text),
|
||||
sa.Column(
|
||||
"order_index", sa.Integer, nullable=False, server_default="0",
|
||||
),
|
||||
sa.Column(
|
||||
"created_at",
|
||||
sa.DateTime(timezone=True),
|
||||
nullable=False,
|
||||
server_default=sa.text("now()"),
|
||||
),
|
||||
sa.Column(
|
||||
"updated_at",
|
||||
sa.DateTime(timezone=True),
|
||||
nullable=False,
|
||||
server_default=sa.text("now()"),
|
||||
),
|
||||
sa.UniqueConstraint("rulebook_id", "title", name="uq_topic_per_rulebook"),
|
||||
)
|
||||
op.create_index(
|
||||
"ix_rulebook_topics_rulebook", "rulebook_topics", ["rulebook_id"],
|
||||
)
|
||||
|
||||
op.create_table(
|
||||
"rules",
|
||||
sa.Column("id", sa.BigInteger, primary_key=True),
|
||||
sa.Column(
|
||||
"topic_id",
|
||||
sa.BigInteger,
|
||||
sa.ForeignKey("rulebook_topics.id", ondelete="CASCADE"),
|
||||
nullable=False,
|
||||
),
|
||||
sa.Column("title", sa.Text, nullable=False),
|
||||
sa.Column("statement", sa.Text, nullable=False),
|
||||
sa.Column("why", sa.Text),
|
||||
sa.Column("how_to_apply", sa.Text),
|
||||
sa.Column(
|
||||
"order_index", sa.Integer, nullable=False, server_default="0",
|
||||
),
|
||||
sa.Column(
|
||||
"created_at",
|
||||
sa.DateTime(timezone=True),
|
||||
nullable=False,
|
||||
server_default=sa.text("now()"),
|
||||
),
|
||||
sa.Column(
|
||||
"updated_at",
|
||||
sa.DateTime(timezone=True),
|
||||
nullable=False,
|
||||
server_default=sa.text("now()"),
|
||||
),
|
||||
sa.UniqueConstraint("topic_id", "title", name="uq_rule_per_topic"),
|
||||
)
|
||||
op.create_index("ix_rules_topic", "rules", ["topic_id"])
|
||||
|
||||
op.create_table(
|
||||
"project_rulebook_subscriptions",
|
||||
sa.Column(
|
||||
"project_id",
|
||||
sa.BigInteger,
|
||||
sa.ForeignKey("projects.id", ondelete="CASCADE"),
|
||||
nullable=False,
|
||||
),
|
||||
sa.Column(
|
||||
"rulebook_id",
|
||||
sa.BigInteger,
|
||||
sa.ForeignKey("rulebooks.id", ondelete="CASCADE"),
|
||||
nullable=False,
|
||||
),
|
||||
sa.Column(
|
||||
"created_at",
|
||||
sa.DateTime(timezone=True),
|
||||
nullable=False,
|
||||
server_default=sa.text("now()"),
|
||||
),
|
||||
sa.PrimaryKeyConstraint("project_id", "rulebook_id"),
|
||||
)
|
||||
op.create_index(
|
||||
"ix_project_rulebook_subs_rulebook",
|
||||
"project_rulebook_subscriptions",
|
||||
["rulebook_id"],
|
||||
)
|
||||
|
||||
|
||||
def downgrade() -> None:
|
||||
op.drop_index(
|
||||
"ix_project_rulebook_subs_rulebook",
|
||||
table_name="project_rulebook_subscriptions",
|
||||
)
|
||||
op.drop_table("project_rulebook_subscriptions")
|
||||
op.drop_index("ix_rules_topic", table_name="rules")
|
||||
op.drop_table("rules")
|
||||
op.drop_index("ix_rulebook_topics_rulebook", table_name="rulebook_topics")
|
||||
op.drop_table("rulebook_topics")
|
||||
op.drop_index("ix_rulebooks_owner", table_name="rulebooks")
|
||||
op.drop_table("rulebooks")
|
||||
Reference in New Issue
Block a user