"""Give every report a level on arrival, and remember nobody confirmed it. 「级别默认初级 不要让人再选了」 — 130 of 149 reports sat ungraded, and grading was a gate: the reward tier, the fix deadline and the reply letter all read from it, so a report with no level could not move at all. Making a person pick one before anything else could happen is why the queue never drained. So a new report arrives at the LOWEST tier (``informational`` — $0, no fix SLA), which commits no money and imposes no deadline, and the workflow proceeds. ``severity_is_default`` is what keeps that safe. Without it the console cannot tell "somebody looked and said informational" from "nobody has looked", and two things would quietly go wrong: * the reply composer pre-selects a letter from the tier, so a genuinely critical finding would be offered the **$0 informational reward letter** — and a reward stated to a researcher cannot be taken back; * 「待定级」 would read 0 while nothing had been triaged, which is the gap-number-nobody-believes failure the console was rebuilt to end. With the flag, a defaulted level still routes and still displays, the letter stays "under evaluation" until a human confirms the grade, and 「待定级」 counts reports still sitting on the default. Any explicit ``set_severity`` clears it — including one that re-picks informational, because that is a person deciding. Existing rows are NOT back-filled to informational: a level nobody chose, written across 130 historical reports, would be indistinguishable from triage that happened. They keep ``severity IS NULL`` and the console shows them as ungraded, which is true. Revision ID: 551 Revises: 550 """ from __future__ import annotations import sqlalchemy as sa from alembic import op revision = "551" down_revision = "550" branch_labels = None depends_on = None TABLE = "bounty_reports" COLUMN = "severity_is_default" def _has_column(conn: sa.engine.Connection) -> bool: return bool( conn.execute( sa.text( "SELECT COUNT(*) FROM information_schema.columns " "WHERE table_schema = DATABASE() " "AND table_name = :t AND column_name = :c", ), {"t": TABLE, "c": COLUMN}, ).scalar() ) def upgrade() -> None: conn = op.get_bind() if not sa.inspect(conn).has_table(TABLE): return if _has_column(conn): return op.add_column( TABLE, sa.Column( COLUMN, sa.Boolean(), nullable=False, server_default=sa.text("0"), comment=( "True while `severity` is the level assigned on arrival " "rather than one a person chose. Keeps the reply composer " "off the tier's reward letter and keeps the report counted " "in 待定级." ), ), ) # Every existing row keeps severity_is_default = 0. The 130 ungraded # ones have severity NULL, so the flag is meaningless for them; the 19 # graded ones were graded by a person, which is exactly what 0 means. def downgrade() -> None: conn = op.get_bind() if not sa.inspect(conn).has_table(TABLE): return if not _has_column(conn): return # No index on this column, so a plain drop is safe on TiDB too. op.drop_column(TABLE, COLUMN)