From 0e9ceb1c318ae9ae99b4aa4f6c50be4256ef3e4e Mon Sep 17 00:00:00 2001 From: Hizrian Date: Fri, 29 May 2026 13:07:02 +0700 Subject: [PATCH] =?UTF-8?q?feat(api):=20AIN-303=20=C2=B7=20routing=5Foutco?= =?UTF-8?q?mes.source=20for=20synthetic/shadow=20wall=20(0036)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Adds a `source` discriminator (prod|synthetic|shadow, NOT NULL default 'prod', CHECK + index) so the synthetic cold-start loop's rows can never feed a prod routing-policy promotion โ€” prod refits filter source='prod'. Existing 147 real rows backfill to 'prod'. Additive; Disc #12 intact. Co-Authored-By: Claude Opus 4.8 (1M context) --- .../20260529_0036_ain303_outcome_source.py | 50 +++++++++++++++++++ 1 file changed, 50 insertions(+) create mode 100644 alembic/versions/20260529_0036_ain303_outcome_source.py diff --git a/alembic/versions/20260529_0036_ain303_outcome_source.py b/alembic/versions/20260529_0036_ain303_outcome_source.py new file mode 100644 index 0000000..15e6bf3 --- /dev/null +++ b/alembic/versions/20260529_0036_ain303_outcome_source.py @@ -0,0 +1,50 @@ +"""AIN-303 ยท routing_outcomes.source โ€” synthetic/shadow tagging (INVARIANT 1). + +Revision ID: 20260529_0036 +Revises: 20260529_0035 +Create Date: 2026-05-29 + +The synthetic cold-start loop writes outcomes that must NEVER feed a prod +routing-policy promotion. This adds a `source` discriminator so the wall is +enforceable in SQL: prod-policy refits filter `source = 'prod'`; the synthetic +warmup loop tags its rows `source = 'synthetic'` (and shadow-replay rows +`'shadow'`). Existing rows are real traffic โ†’ backfilled to 'prod'. + +Additive: NOT NULL with server_default 'prod' (existing 147 rows become 'prod'), +a CHECK constraint pinning the vocabulary, and an index for the source-filtered +corpus reads. No scoring/auth/candidate-set change (Disc #12 intact). +""" + +from __future__ import annotations + +import sqlalchemy as sa + +from alembic import op + +revision = "20260529_0036" +down_revision = "20260529_0035" +branch_labels = None +depends_on = None + + +def upgrade() -> None: + op.add_column( + "routing_outcomes", + sa.Column("source", sa.Text(), nullable=False, server_default="prod"), + ) + op.create_check_constraint( + "ck_routing_outcomes_source", + "routing_outcomes", + "source IN ('prod','synthetic','shadow')", + ) + op.create_index( + "ix_routing_outcomes_source", + "routing_outcomes", + ["source"], + ) + + +def downgrade() -> None: + op.drop_index("ix_routing_outcomes_source", table_name="routing_outcomes") + op.drop_constraint("ck_routing_outcomes_source", "routing_outcomes", type_="check") + op.drop_column("routing_outcomes", "source")