diff --git a/crates/core/src/api.rs b/crates/core/src/api.rs index ee9ac15..627ef76 100644 --- a/crates/core/src/api.rs +++ b/crates/core/src/api.rs @@ -464,10 +464,39 @@ pub fn discover_route( _ => start_fl, }; - let result = routing::discover::find_valid_route( + let mut result = routing::discover::find_valid_route( &conn, &from, &to, &dep_sid, &dest_star, start_fl, rad, validator, )?; + // Low-regime fallback: if the upper route is blocked by RAD/FRA restrictions + // (PROF204 forbidden / PROF205 mandatory routing) we can't model, retry in the + // conventional airway regime *below* the FRA floor (~FL195). The low network + // has far fewer such restrictions (validated: EGLL–EDDM 9→1 err), so this + // trades cruise altitude for a valid plan. Keep whichever result is better. + const FRA_FLOOR: i32 = 195; + let fra_blocked = !result.accepted + && result.errors.iter().any(|e| e.code == "PROF204" || e.code == "PROF205"); + if fra_blocked && start_fl >= FRA_FLOOR { + let low_target = full + .as_ref() + .and_then(|r| routing::route_fl_ceiling(&conn, r).ok().flatten()) + .map_or(FRA_FLOOR - 15, |c| c.min(FRA_FLOOR - 15)) + / 10 + * 10; + if low_target >= 60 && low_target < start_fl { + let low = routing::discover::find_valid_route( + &conn, &from, &to, &dep_sid, &dest_star, low_target, rad, validator, + )?; + // Only adopt the low route if it is *fully valid* — the bar is no-error. + // A low route with fewer-but-nonzero errors is no more deliverable than + // the natural-FL one, and flying needlessly low isn't the right answer, + // so keep the natural route in that case. + if low.accepted { + result = low; + } + } + } + // Only store routes the oracle actually accepted (no-error) — the DB is the // set of IFPS-valid routes, nothing else. if result.accepted {