From 49dc74ef20427ef82c06dc42e9b4bbf6798c57ee Mon Sep 17 00:00:00 2001 From: Alexandre Date: Sat, 22 Aug 2026 16:49:54 +0200 Subject: [PATCH] feat(routing): low-regime fallback when the upper route is FRA-blocked MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit If discovery's upper-airspace route can't be made no-error because of RAD restrictions we don't yet model (PROF204 forbidden / PROF205 mandatory routing common in Free Route Airspace), retry the whole discovery in the conventional airway regime below the FRA floor (~FL195), where those restrictions are far sparser. Adopt the low route only if it is fully valid — the bar is no-error, and flying needlessly low isn't the answer when neither passes, so the natural-FL route is kept otherwise. Measured on the batch: no regression, and the fallback collapses the FRA-blocked pairs from ~7-9 errors to 1-2 (near-miss). It delivers a no-error plan for any pair that has a valid low-level alternative; the remaining near-misses need the phase-2 mandatory-routing engine to close. 49 tests green. Co-Authored-By: Claude Opus 4.8 --- crates/core/src/api.rs | 31 ++++++++++++++++++++++++++++++- 1 file changed, 30 insertions(+), 1 deletion(-) 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 {