# SPDX-License-Identifier: Apache-2.0 # Copyright 2026 DriftCall Team # Authoritative catalogue: 20 drift patterns per DESIGN.md §6.3 and # docs/modules/drift_injector.md §4.4 (5 schema + 5 policy + 5 T&C + 3 pricing # + 2 transversal payment-auth). # Every id MUST match drift_injector.md §4.4 exactly. # Every from_version / to_version MUST be a v.json under data/api_schemas//. # detection_hints are substring-matchable tokens (DESIGN.md §6.3). # --- Schema drifts (5) ------------------------------------------------------- - id: airline.price_rename drift_type: schema domain: airline from_version: v1 to_version: v2 description: "field 'price' renamed to 'total_fare_inr'; 'currency' removed" mutation: rename: {price: total_fare_inr} remove: [currency] detection_hints: - total_fare_inr - price - rename - id: airline.pax_required drift_type: schema domain: airline from_version: v2 to_version: v3 description: "booking now requires 'passenger_count' field" mutation: require_new_field: [passenger_count] detection_hints: - passenger_count - MISSING_PASSENGER_COUNT - passenger - id: cab.fare_breakdown drift_type: schema domain: cab from_version: v2 to_version: v3 description: "field 'fare_inr' replaced by nested 'fare_breakdown' with base/surge/tolls/gst" mutation: remove: [fare_inr] require_new_field: [fare_breakdown, total_inr] detection_hints: - fare_breakdown - base - surge - tolls - gst - id: restaurant.items_shape_bump drift_type: schema domain: restaurant from_version: v2 to_version: v3 description: "each item in 'items' now requires a 'modifiers' list (empty allowed)" mutation: require_new_field: [modifiers] detection_hints: - modifiers - INVALID_ITEMS_SHAPE - items - id: hotel.gst_field drift_type: schema domain: hotel from_version: v2 to_version: v3 description: "hotel.book now requires 'gst_number' when total_with_tax > 7500" mutation: require_new_field: [gst_number] detection_hints: - gst_number - MISSING_GST_NUMBER - GST_REQUIRED # --- Policy drifts (5) ------------------------------------------------------- - id: airline.booking_window_shrink drift_type: policy domain: airline from_version: v1 to_version: v2 description: "same-day bookings rejected after 14:00 IST (was: same-day always allowed)" mutation: time_window_shrink: {same_day_cutoff_hour_ist: 14} detection_hints: - BOOKING_WINDOW_CLOSED - booking_window - "14:00" - same-day - id: cab.school_hours_mini_reject drift_type: policy domain: cab from_version: v1 to_version: v2 description: "vehicle_class=mini during 07:00-09:00 IST now auto-rejects with policy_error" mutation: policy_flag_flip: {mini_reject_school_hours: true} detection_hints: - SCHOOL_HOURS_MINI_REJECTED - school_hours - mini - "07:00" - id: restaurant.min_order_bump drift_type: policy domain: restaurant from_version: v1 to_version: v2 description: "minimum order amount increased from 199 to 299" mutation: numeric_bump: {min_order_inr: 299} detection_hints: - MIN_ORDER_NOT_MET - min_order - "299" - id: hotel.cancel_window_shrink drift_type: policy domain: hotel from_version: v1 to_version: v2 description: "free cancellation window shrunk from 24h to 6h before check-in" mutation: time_window_shrink: {cancel_window_hours: 6} detection_hints: - CANCEL_WINDOW_EXPIRED - cancel_window - 6h - 24h - id: cab.vehicle_class_expand drift_type: policy domain: cab from_version: v1 to_version: v2 description: "vehicle_class enum expanded to include 'suv' and 'infant_seat_sedan'" mutation: enum_expand: vehicle_class: [mini, sedan, suv, infant_seat_sedan] detection_hints: - vehicle_class - suv - infant_seat_sedan - VEHICLE_CLASS_UNAVAILABLE # --- T&C drifts (5) --------------------------------------------------------- - id: airline.baggage_tnc_rewrite drift_type: tnc domain: airline from_version: v1 to_version: v2 description: "free cabin allowance reduced from 7kg to 5kg; announced via side-channel notice" mutation: tnc_text_swap: {cabin_allowance_kg: 5} side_channel_notice_append: "Baggage T&C updated: free cabin allowance is now 5kg (was 7kg)." detection_hints: - cabin_allowance - 5kg - 7kg - baggage - id: cab.surge_policy_tnc drift_type: tnc domain: cab from_version: v1 to_version: v2 description: "surge may now apply retroactively if ride is extended; side-channel notice" mutation: tnc_text_swap: {surge_retroactive: true} side_channel_notice_append: "Surge policy updated: retroactive surge may apply if ride is extended." detection_hints: - surge - retroactive - surge_retroactive - id: restaurant.veg_filter_semantic drift_type: tnc domain: restaurant from_version: v2 to_version: v3 description: "veg_only=True now excludes egg-based dishes (previously included)" mutation: # Two coupled keys: veg_only_excludes_egg is the new boolean policy flag # introduced in v3; veg_only is the user-facing slot whose semantics are # being mutated by this drift (same field, different meaning). Templates # tag the live slot (veg_only) — keep both so the slot_tag is targetable # AND the policy-flag audit trail is preserved. tnc_text_swap: {veg_only_excludes_egg: true, veg_only: "semantics_egg_excluded"} side_channel_notice_append: "Semantics update: veg_only=True now excludes egg-based dishes." detection_hints: - veg_only - egg - semantic - id: hotel.early_checkin_tnc drift_type: tnc domain: hotel from_version: v1 to_version: v2 description: "early check-in before 12:00 now billed at 50% of nightly rate; side-channel" mutation: tnc_text_swap: {early_checkin_fee_pct: 50} side_channel_notice_append: "Early check-in before 12:00 now costs 50% of nightly rate." detection_hints: - early_checkin - 50% - "12:00" - nightly - id: airline.reschedule_tnc drift_type: tnc domain: airline from_version: v2 to_version: v3 description: "reschedule fee previously waived; now 10% of fare + side-channel text" mutation: tnc_text_swap: {reschedule_fee_pct: 10} side_channel_notice_append: "Reschedule fee policy updated: 10% of fare applies." detection_hints: - reschedule - 10% - fare # --- Pricing drifts (3) ----------------------------------------------------- - id: airline.convenience_fee_append drift_type: pricing domain: airline from_version: v1 to_version: v2 description: "hidden 199 INR convenience fee added at booking; search estimate unchanged" mutation: fee_append: {convenience_fee_inr: 199} detection_hints: - convenience_fee - "199" - hidden - id: cab.toll_unbundle drift_type: pricing domain: cab from_version: v1 to_version: v2 description: "tolls previously included; now a separate line item on booking, not estimate" mutation: pricing_restructure: {toll_bundled: false} detection_hints: - toll - tolls - unbundle - id: hotel.resort_fee_append drift_type: pricing domain: hotel from_version: v1 to_version: v2 description: "500 INR/night resort fee added at booking; not shown in nightly_rate" mutation: fee_append: {resort_fee_inr: 500} detection_hints: - resort_fee - "500" - resort # --- Auth drifts (2, transversal via payment) ------------------------------- - id: payment.auth_scope_upgrade drift_type: auth domain: payment from_version: v1 to_version: v2 description: "token_v1 now returns 401; requires token_v2 with scope=payments:write:v2" mutation: auth_scope_bump: {required_scope: "payments:write:v2"} token_version_bump: {accepted_token_version: v2} detection_hints: - AUTH_SCOPE_INSUFFICIENT - scope - token_v2 - "payments:write:v2" - id: payment.mfa_required drift_type: auth domain: payment from_version: v1 to_version: v2 description: "transactions > 5000 INR now require mfa_code; auth_error with MFA_REQUIRED (payment v1 → v2, alternative auth-tightening to auth_scope_upgrade triggered by amount rather than scope)" mutation: policy_flag_flip: {mfa_required: true} numeric_bump: {mfa_threshold_inr: 5000} detection_hints: - MFA_REQUIRED - mfa_code - mfa - "5000"