Files
FabledGrimoire/alembic/versions/0055_rulebook_tables.py
T
bvandeusenandClaude Opus 5.5 79655fd19f
CI & Build / Python lint (push) Successful in 2s
CI & Build / Plugin hooks (push) Successful in 13s
CI & Build / TypeScript typecheck (push) Successful in 59s
CI & Build / integration (push) Successful in 1m32s
CI & Build / Python tests (push) Successful in 2m33s
CI & Build / Build & push image (push) Successful in 26s
docs(6207): the remaining migrations state their rationale in terms of the data
Record, milestone, phase and plan citations go from every migration header;
the "end before start" fix (0043) and the suppressed-count column (0095)
state the condition they prevent rather than the incident. 0053 and 0055
pointed at design specs under docs/superpowers that are not in the repo;
those pointers are removed.

Kept: alembic's generated "Create Date:" headers (template metadata, not
rationale), 0121's ARM_BORN justification beside the constant, and 0051's
illustrative "update task 42" format example.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-21 20:49:32 -04:00

154 lines
4.6 KiB
Python

"""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; migration 0101
later drops subscriptions).
"""
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")