"""IMP-33 u1 — AI fallback Settings defaults (locked). These defaults are the binding contract from Stage 2 plan (per-unit u1): - ai_fallback_enabled = False (master flag OFF; fallback path only) - ai_fallback_model = "claude-opus-4-7" - ai_fallback_timeout_s = 60.0 - ai_fallback_max_retries = 3 - ai_fallback_backoff_base_s = 1.0 - ai_fallback_backoff_cap_s = 8.0 - ai_fallback_backoff_jitter = 0.3 - ai_fallback_budget_per_run = 10 - ai_fallback_circuit_breaker_threshold = 5 Downstream u4 (client) MUST source timeout/retry/backoff/budget/circuit from Settings; inline literals are forbidden by Stage 2 plan. """ from __future__ import annotations from src.config import Settings def test_ai_fallback_master_flag_default_off() -> None: s = Settings() assert s.ai_fallback_enabled is False, ( "AI fallback master flag MUST default OFF (normal path AI=0 contract)." ) def test_ai_fallback_model_default_locked() -> None: s = Settings() assert s.ai_fallback_model == "claude-opus-4-7" def test_ai_fallback_retry_timeout_backoff_defaults_locked() -> None: s = Settings() assert s.ai_fallback_timeout_s == 60.0 assert s.ai_fallback_max_retries == 3 assert s.ai_fallback_backoff_base_s == 1.0 assert s.ai_fallback_backoff_cap_s == 8.0 assert s.ai_fallback_backoff_jitter == 0.3 def test_ai_fallback_budget_and_circuit_defaults_locked() -> None: s = Settings() assert s.ai_fallback_budget_per_run == 10 assert s.ai_fallback_circuit_breaker_threshold == 5 # IMP-46 u5 — auto-cache opt-in setting default lock. # The CLI flag ``--auto-cache`` in src/phase_z2_pipeline.py mutates this # setting at parse time. The default MUST stay OFF so the dual-gate # contract (visual_check_passed AND user_approved) survives without an # explicit operator opt-in. def test_ai_fallback_auto_cache_default_off() -> None: s = Settings() assert s.ai_fallback_auto_cache is False, ( "IMP-46 u5 auto-cache MUST default OFF; the dual-gate contract " "(visual_check_passed AND user_approved) survives without an " "explicit --auto-cache opt-in." ) # IMP-47B u1 — reject route hint policy correction. # Prior to 2026-05-21 the reject V4 label routed to ``design_reference_only`` # (no AI). The user policy correction (issue #76) reroutes reject to # ``ai_adaptation_required`` so the rank-1 reject frame is kept and the AI # re-maps MDX content into its declared slots. Activation remains gated by # ``ai_fallback_enabled`` (default OFF preserves the normal-path AI=0 # contract — see test_ai_fallback_master_flag_default_off above). def test_reject_route_hint_routes_to_ai_adaptation() -> None: from src.phase_z2_pipeline import _IMP05_ROUTE_HINTS, _imp05_route_hint assert _IMP05_ROUTE_HINTS["reject"] == "ai_adaptation_required", ( "IMP-47B u1: reject must route to ai_adaptation_required so the " "rank-1 reject frame is retained and AI re-maps MDX content into " "its slots (frame auto-swap forbidden)." ) assert _imp05_route_hint("reject") == "ai_adaptation_required" # Sibling routes unchanged — guardrail against accidental drift. assert _imp05_route_hint("use_as_is") == "direct_render" assert _imp05_route_hint("light_edit") == "deterministic_minor_adjustment" assert _imp05_route_hint("restructure") == "ai_adaptation_required"