feat(routes): export/import validated routes — make the 100% DB permanent

The oracle-validated route DB (the realistic path to 100% under prose-only)
lived only in the gitignored real.db, so a navdata rebuild would lose it.
Now it is portable and version-controlled:

- routes::export_validated -> TSV of every ifps_ok route (one per pair);
  import_seeds loads them back as reusable seeds (source=seed).
- CLI `export-routes` / `import-routes`.
- discover reuse now matches any ifps_ok route (not just source=ifps), so
  imported seeds are reused instantly (verified round-trip).
- data/validated_routes.tsv: 65 validated European city-pair routes, built
  by bulk-seeding discover over ~85 common pairs (the DB grew ~11 -> 65).

This is the PFPX-style route DB, committed: 65 pairs are now no-error &
instant on any machine; `learn` + more bulk-seed grow it further.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-08-28 21:45:29 +02:00
parent 36ecaed267
commit 89270c01a3
4 changed files with 161 additions and 1 deletions
+58
View File
@@ -97,3 +97,61 @@ pub fn recent(conn: &Connection, dep: &str, dest: &str, limit: usize) -> Result<
pub fn count(conn: &Connection) -> Result<i64> {
Ok(conn.query_row("SELECT COUNT(*) FROM routes", [], |r| r.get(0))?)
}
/// Export every IFPS-validated (`ifps_ok`) route as TSV lines
/// (`dep⇥dest⇥fl⇥dist⇥via_airways⇥route_string`). This is the portable, version-
/// controllable form of the route DB, so validated seeds survive a navdata rebuild
/// (the `routes` table lives in the same SQLite file as the imported navdata).
pub fn export_validated(conn: &Connection) -> Result<String> {
let mut stmt = conn.prepare(
"SELECT dep,dest,cruise_fl,dist_nm,via_airways,route_string FROM routes \
WHERE ifps_ok=1 GROUP BY dep,dest ORDER BY dep,dest",
)?;
let rows = stmt.query_map([], |r| {
Ok(format!(
"{}\t{}\t{}\t{:.0}\t{}\t{}",
r.get::<_, String>(0)?,
r.get::<_, String>(1)?,
r.get::<_, i32>(2)?,
r.get::<_, f64>(3)?,
r.get::<_, i32>(4)?,
r.get::<_, String>(5)?,
))
})?;
let mut out = String::new();
for row in rows {
out.push_str(&row?);
out.push('\n');
}
Ok(out)
}
/// Import validated routes from TSV (as produced by [`export_validated`]),
/// recording each as an `ifps_ok` seed (`source="seed"`). Returns the count.
pub fn import_seeds(conn: &Connection, tsv: &str) -> Result<usize> {
let mut n = 0;
for line in tsv.lines() {
let line = line.trim();
if line.is_empty() {
continue;
}
let f: Vec<&str> = line.splitn(6, '\t').collect();
if f.len() < 6 {
continue;
}
record(
conn,
f[0],
f[1],
f[2].parse().unwrap_or(0),
f[5],
f[3].parse().unwrap_or(0.0),
f[4] == "1",
true,
&[],
"seed",
)?;
n += 1;
}
Ok(n)
}