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>
64 lines
2.5 KiB
Python
64 lines
2.5 KiB
Python
"""rule_usage_events carry an outcome, not just a read
|
|
|
|
Revision ID: 0106
|
|
Revises: 0105
|
|
Create Date: 2026-09-20
|
|
|
|
`rule_usage_events` can say a rule was SURFACED
|
|
and that it was PULLED. It cannot say what happened next, so these two
|
|
sessions leave identical telemetry:
|
|
|
|
- a rule surfaced, opened, and followed;
|
|
- a rule surfaced, opened, and silently ignored.
|
|
|
|
The second is the more urgent by a distance, and it is the one the readout
|
|
cannot name, and it is what an outcome column is for.
|
|
|
|
TWO NEW EVENT VALUES, AND NO CHECK MIGRATION. `event` was created in 0094 as
|
|
plain `sa.Text()` with no constraint — checked in the migration itself, not
|
|
assumed from the model — so `applied` and `departed` join `surfaced` and
|
|
`pulled` without a DROP/ADD pair. That pair is for CHECK-whitelisted columns
|
|
and this is not one; noted explicitly because the next reader will reach for
|
|
it here, and should be able to see in one place why it does not apply.
|
|
|
|
THE THIRD STATE IS DERIVED, AND THAT IS NOT A SHORTCUT. Read-and-silently-
|
|
unchanged is the absence of an outcome, and it has to be: an agent that knew
|
|
it was ignoring a rule would not be ignoring it. There is no honest way to ask
|
|
for that event, so nothing here tries. `applied` and `departed` are reported;
|
|
the third state is what is left over when a rule was pulled and neither
|
|
arrived. A schema that offered an `ignored` value would collect nothing and
|
|
read as though it had measured something — a statistic that cannot vary
|
|
being mistaken for a finding.
|
|
|
|
`detail` CARRIES THE WHY OF A DEPARTURE, and is the reason this is a column
|
|
rather than two more bare event strings. A departure without its reason is
|
|
indistinguishable from a miss when someone reads the table back, so the two
|
|
states the milestone wants to tell apart would collapse again one layer down.
|
|
Nullable because `applied` needs no argument — following a rule is the
|
|
unremarkable case, and demanding prose for it would make the cheap event
|
|
expensive and stop it being recorded at all.
|
|
|
|
NO NEW INDEX. Every outcome readout starts from a set of rule ids and narrows
|
|
by event, which is exactly `ix_rule_usage_rule_event` (rule_id, event) from
|
|
0094. Adding a `detail` index would serve no query anyone has — the column is
|
|
read, never filtered on.
|
|
"""
|
|
import sqlalchemy as sa
|
|
from alembic import op
|
|
|
|
revision = "0106"
|
|
down_revision = "0105"
|
|
branch_labels = None
|
|
depends_on = None
|
|
|
|
|
|
def upgrade() -> None:
|
|
op.add_column(
|
|
"rule_usage_events",
|
|
sa.Column("detail", sa.Text(), nullable=True),
|
|
)
|
|
|
|
|
|
def downgrade() -> None:
|
|
op.drop_column("rule_usage_events", "detail")
|