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
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>
97 lines
3.7 KiB
Python
97 lines
3.7 KiB
Python
"""drop the always-on tier: rules.tier, rulebooks.always_on, the exclusions table
|
|
|
|
Revision ID: 0100
|
|
Revises: 0099
|
|
Create Date: 2026-09-11
|
|
|
|
Every rule reaches a session by retrieval — because
|
|
something it is about to do made the rule relevant — and the machinery that
|
|
delivered rules unconditionally goes with it.
|
|
|
|
WHAT GOES, AND WHERE IT CAME FROM
|
|
|
|
- ``rules.tier`` and its ``ck_rules_tier`` CHECK (migration 0088). Dropping
|
|
the column takes the constraint with it. DROP + ADD is about ADDING a value to
|
|
a live whitelist, which needs DROP + ADD in the same migration; it does not
|
|
speak to removing the column outright, and saying so here is cheaper than
|
|
the next reader wondering whether it was forgotten.
|
|
- ``rule_versions.tier`` (migration 0098). A version records what a rule
|
|
SAID; with no tier on a rule there is nothing for a snapshot to carry.
|
|
- ``rulebooks.always_on`` (migration 0058). A rulebook reaches a project by
|
|
subscription now, and by nothing else.
|
|
- ``project_rulebook_exclusions`` (migration 0085). It recorded a project's
|
|
opt-out of an always-on rulebook. Opting out of something that no longer
|
|
binds you is not a state that can exist — declining a rulebook is
|
|
expressed by not subscribing to it.
|
|
|
|
IRREVERSIBLE, AND THE DOWNGRADE SAYS SO RATHER THAN PRETENDING
|
|
|
|
The downgrade recreates the columns and the table with their DEFAULTS. It
|
|
cannot restore WHICH rules were always-on, which rulebooks bound every project,
|
|
or which projects had opted out — that information is in what this drops.
|
|
|
|
That distinction is the one this repo keeps insisting on: a value invented to
|
|
fill a hole is not a measurement. So a downgraded database is structurally able
|
|
to run the old code and is NOT the database the old code was running against —
|
|
every rule comes back at the ``always_on`` default, which for the tier column
|
|
happens to mean "binding", the safe direction to be wrong in.
|
|
|
|
Anyone who needs the real prior state restores a backup taken before this ran.
|
|
"""
|
|
import sqlalchemy as sa
|
|
from alembic import op
|
|
|
|
revision = "0100"
|
|
down_revision = "0099"
|
|
branch_labels = None
|
|
depends_on = None
|
|
|
|
_TIERS = ("always_on", "conditional")
|
|
|
|
|
|
def _in_list(column: str, values: tuple[str, ...]) -> str:
|
|
return f"{column} IN (" + ", ".join(f"'{v}'" for v in values) + ")"
|
|
|
|
|
|
def upgrade() -> None:
|
|
op.drop_table("project_rulebook_exclusions")
|
|
op.drop_column("rulebooks", "always_on")
|
|
op.drop_column("rule_versions", "tier")
|
|
# The CHECK goes with the column it constrains; naming it here would be a
|
|
# second drop of the same object.
|
|
op.drop_column("rules", "tier")
|
|
|
|
|
|
def downgrade() -> None:
|
|
"""Structure only. See the module docstring — the values are gone."""
|
|
op.add_column(
|
|
"rules",
|
|
sa.Column("tier", sa.Text(), nullable=False, server_default="always_on"),
|
|
)
|
|
op.create_check_constraint("ck_rules_tier", "rules", _in_list("tier", _TIERS))
|
|
op.add_column("rule_versions", sa.Column("tier", sa.Text(), nullable=True))
|
|
op.add_column(
|
|
"rulebooks",
|
|
sa.Column(
|
|
"always_on", sa.Boolean(), nullable=False,
|
|
server_default=sa.text("false"),
|
|
),
|
|
)
|
|
op.create_table(
|
|
"project_rulebook_exclusions",
|
|
sa.Column(
|
|
"project_id", sa.BigInteger(),
|
|
sa.ForeignKey("projects.id", ondelete="CASCADE"),
|
|
primary_key=True, nullable=False,
|
|
),
|
|
sa.Column(
|
|
"rulebook_id", sa.BigInteger(),
|
|
sa.ForeignKey("rulebooks.id", ondelete="CASCADE"),
|
|
primary_key=True, nullable=False,
|
|
),
|
|
sa.Column(
|
|
"created_at", sa.DateTime(timezone=True),
|
|
server_default=sa.text("now()"), nullable=True,
|
|
),
|
|
)
|