Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -37,21 +37,34 @@


def upgrade() -> None:
# Postgres CHECK constraints can't be DEFERRABLE (only FK/UNIQUE/PK/EXCLUDE
# support deferral). The two-phase outcome write (insert_decision phase
# writes the row with decision_rule='cheapest_clearing_floor' and
# inference_id=NULL; complete_decision phase links inference_id) means a
# naive `decision_rule <> 'cheapest_clearing_floor' OR inference_id IS NOT NULL`
# would fire on the initial insert.
# 2026-05-28 PROD note: the human+Claude repair session earlier today
# already added a constraint named
# `outcome_requires_inference_when_model_chosen` with an OLDER predicate
# (`decision_rule <> 'cheapest_clearing_floor' OR inference_id IS NOT NULL`)
# — that predicate breaks the two-phase outcome write because it fires at
# statement time (PG CHECK can't be DEFERRABLE). We DROP it and re-ADD
# with the new predicate (which adds `outcome_status IS NULL` as the
# mid-flight escape clause).
#
# Mitigation: the predicate also allows `outcome_status IS NULL` — the
# marker for "this outcome is mid-flight, complete_decision hasn't run
# yet". Once complete_decision sets outcome_status (always non-NULL on
# every terminal branch — succeeded / failed_other / failed_provider_error
# / rejected_floor / rejected_no_candidates), the constraint REQUIRES
# either (a) the rule was rewritten away from 'cheapest_clearing_floor'
# (pre-dispatch failure → 'failed_pre_dispatch'), or (b) the inference is
# linked. Which is exactly the AIN-300 W1 invariant.
# Idempotent: DROP IF EXISTS so re-running the migration on a fresh DB
# (no prior constraint) is also clean.
op.execute(
"""
ALTER TABLE public.routing_outcomes
DROP CONSTRAINT IF EXISTS outcome_requires_inference_when_model_chosen;
"""
)

# Predicate explanation:
# - `outcome_status IS NULL` → row is mid-flight (insert_decision
# just ran; complete_decision hasn't)
# - `decision_rule <> 'cheapest_clearing_floor'` → no model was chosen
# (reject path / pre-dispatch fail)
# - `inference_id IS NOT NULL` → linkage holds
# By commit time of any terminal branch, outcome_status is non-NULL AND
# either decision_rule was rewritten via decision_rule_override OR
# inference_id is linked. So the constraint REQUIRES the AIN-300 W1
# invariant on committed rows.
op.execute(
"""
ALTER TABLE public.routing_outcomes
Expand Down
Loading