7861607fb8
Lets a project mute individual rules or whole topics from rulebooks it subscribes to, without unsubscribing the rulebook. Two new association tables (migration 0060), 4 MCP tools (suppress/unsuppress × rule/topic), 4 REST endpoints, and an inline "× skip" affordance plus collapsed "Suppressed (N)" section in the project's Rules tab. get_applicable_rules now emits suppressed_rules and suppressed_topics (detail objects with rulebook/topic context, not just IDs) so the UI can render the suppressed list without a follow-up lookup. The main rules projection grew topic_id and rulebook_id columns for the per-row suppress affordance. Project deletion cascades the suppression rows via hard DELETE — they are pure associations with no soft-delete column, and restoring a deleted project should start fresh, not inherit stale mutes. Project-scoped rules (Rule.project_id) are deliberately not suppressible — delete them with delete_rule instead. Implements plan-task #187. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
74 lines
1.9 KiB
Python
74 lines
1.9 KiB
Python
"""project rule + topic suppressions
|
|
|
|
Revision ID: 0060
|
|
Revises: 0059
|
|
Create Date: 2026-06-01
|
|
|
|
Lets a project mute specific rules or whole topics from rulebooks it
|
|
subscribes to, without unsubscribing the rulebook. Two pure many-to-many
|
|
association tables; FKs CASCADE so removing a project / rule / topic
|
|
cleans the suppression rows automatically.
|
|
"""
|
|
from alembic import op
|
|
import sqlalchemy as sa
|
|
|
|
|
|
revision = "0060"
|
|
down_revision = "0059"
|
|
branch_labels = None
|
|
depends_on = None
|
|
|
|
|
|
def upgrade() -> None:
|
|
op.create_table(
|
|
"project_rule_suppressions",
|
|
sa.Column(
|
|
"project_id",
|
|
sa.BigInteger(),
|
|
sa.ForeignKey("projects.id", ondelete="CASCADE"),
|
|
primary_key=True,
|
|
nullable=False,
|
|
),
|
|
sa.Column(
|
|
"rule_id",
|
|
sa.BigInteger(),
|
|
sa.ForeignKey("rules.id", ondelete="CASCADE"),
|
|
primary_key=True,
|
|
nullable=False,
|
|
),
|
|
sa.Column(
|
|
"created_at",
|
|
sa.DateTime(timezone=True),
|
|
server_default=sa.text("now()"),
|
|
nullable=False,
|
|
),
|
|
)
|
|
op.create_table(
|
|
"project_topic_suppressions",
|
|
sa.Column(
|
|
"project_id",
|
|
sa.BigInteger(),
|
|
sa.ForeignKey("projects.id", ondelete="CASCADE"),
|
|
primary_key=True,
|
|
nullable=False,
|
|
),
|
|
sa.Column(
|
|
"topic_id",
|
|
sa.BigInteger(),
|
|
sa.ForeignKey("rulebook_topics.id", ondelete="CASCADE"),
|
|
primary_key=True,
|
|
nullable=False,
|
|
),
|
|
sa.Column(
|
|
"created_at",
|
|
sa.DateTime(timezone=True),
|
|
server_default=sa.text("now()"),
|
|
nullable=False,
|
|
),
|
|
)
|
|
|
|
|
|
def downgrade() -> None:
|
|
op.drop_table("project_topic_suppressions")
|
|
op.drop_table("project_rule_suppressions")
|