fix(simulation): address PR #108 review — LEFT JOIN, JSON output, atmosphere default
Review fixes: - corridor-status: INNER JOIN → LEFT JOIN so systems without gate records are included in counts instead of silently excluded - corridor-status: output as JSON (serde_json) matching all other atlas commands, instead of plain-text ASCII table - generate_body_matrix: emit atmosphere "standard" instead of "breathable" to match committed-system conventions - Doc header: add corridor-status usage example - CHANGELOG: note stale habitable_planet_count in pre-fix systems Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
+3
-1
@@ -10,7 +10,9 @@ Format based on [Keep a Changelog](https://keepachangelog.com/).
|
||||
- `corridor-status` subcommand for atlas CLI — shows remaining unfinished systems grouped by geographic sector and hop distance (#744)
|
||||
|
||||
### Fixed
|
||||
- `habitable_planet_count` filter now accepts both "breathable" and "standard" atmosphere values — previously all committed systems reported 0 habitable planets (#762)
|
||||
- `habitable_planet_count` filter now accepts both "breathable" and "standard" atmosphere values — previously all committed systems reported 0 habitable planets (#762). Systems committed before this fix may have stale `habitable_planet_count = 0`; re-commit to update.
|
||||
- `corridor-status` uses LEFT JOIN so systems without gate records are included in counts
|
||||
- `generate_body_matrix` now emits `atmosphere: "standard"` (was "breathable") to match committed-system conventions
|
||||
|
||||
## [v0.1.29] — 2026-04-03
|
||||
|
||||
|
||||
+37
-26
@@ -10,6 +10,7 @@
|
||||
//! tooling/atlas show-body "GJ 15Ab"
|
||||
//! tooling/atlas add-body --system "GJ 15A" --type planet --orbit 1 --id "GJ 15Ab"
|
||||
//! tooling/atlas stats
|
||||
//! tooling/atlas corridor-status # remaining systems by corridor/hop
|
||||
//! tooling/atlas populate # bulk classifier pass
|
||||
//!
|
||||
//! # Direct:
|
||||
@@ -911,7 +912,7 @@ fn generate_body_matrix(
|
||||
surface_gravity: Some(0.9),
|
||||
orbital_period_days: Some(365.0),
|
||||
rotation_period_hours: Some(24.0),
|
||||
atmosphere: Some("breathable".into()),
|
||||
atmosphere: Some("standard".into()),
|
||||
biome_summary: Some("temperate".into()),
|
||||
hydrosphere: None,
|
||||
economic_role: None,
|
||||
@@ -943,7 +944,7 @@ fn generate_body_matrix(
|
||||
surface_gravity: Some(0.85),
|
||||
orbital_period_days: Some(400.0),
|
||||
rotation_period_hours: Some(26.0),
|
||||
atmosphere: Some("breathable".into()),
|
||||
atmosphere: Some("standard".into()),
|
||||
biome_summary: Some("temperate".into()),
|
||||
hydrosphere: None,
|
||||
economic_role: None,
|
||||
@@ -1999,26 +2000,28 @@ fn format_population(pop: i64) -> String {
|
||||
fn cmd_corridor_status(conn: &Connection) {
|
||||
// Query unfinished systems grouped by geographic_sector and hop_distance_from_gateway.
|
||||
// "Unfinished" = no rows in bodies for this system_id.
|
||||
// LEFT JOIN so systems with no gate record are still counted (hop = NULL → "?").
|
||||
let mut stmt = conn
|
||||
.prepare(
|
||||
"SELECT s.geographic_sector, g.hop_distance_from_gateway, COUNT(*) AS remaining
|
||||
FROM star_systems s
|
||||
JOIN system_gates g ON s.system_id = g.system_id
|
||||
LEFT JOIN system_gates g ON s.system_id = g.system_id
|
||||
WHERE s.system_id NOT IN (SELECT DISTINCT system_id FROM bodies)
|
||||
GROUP BY s.geographic_sector, g.hop_distance_from_gateway
|
||||
ORDER BY s.geographic_sector, g.hop_distance_from_gateway",
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
struct Row {
|
||||
#[derive(Serialize)]
|
||||
struct CorridorRow {
|
||||
sector: Option<String>,
|
||||
hop: Option<i32>,
|
||||
remaining: i64,
|
||||
}
|
||||
|
||||
let rows: Vec<Row> = stmt
|
||||
let rows: Vec<CorridorRow> = stmt
|
||||
.query_map([], |row| {
|
||||
Ok(Row {
|
||||
Ok(CorridorRow {
|
||||
sector: row.get(0)?,
|
||||
hop: row.get(1)?,
|
||||
remaining: row.get(2)?,
|
||||
@@ -2029,31 +2032,39 @@ fn cmd_corridor_status(conn: &Connection) {
|
||||
.collect();
|
||||
|
||||
if rows.is_empty() {
|
||||
println!("All systems have bodies authored.");
|
||||
#[derive(Serialize)]
|
||||
struct EmptyResult {
|
||||
total: i64,
|
||||
message: &'static str,
|
||||
corridors: Vec<()>,
|
||||
}
|
||||
println!(
|
||||
"{}",
|
||||
serde_json::to_string_pretty(&EmptyResult {
|
||||
total: 0,
|
||||
message: "All systems have bodies authored.",
|
||||
corridors: vec![],
|
||||
})
|
||||
.unwrap()
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
// Plain text table: corridor | hop | remaining
|
||||
let total: i64 = rows.iter().map(|r| r.remaining).sum();
|
||||
println!("{:<30} {:>4} {:>9}", "Corridor", "Hop", "Remaining");
|
||||
println!("{}", "-".repeat(48));
|
||||
let mut last_sector = String::new();
|
||||
for row in &rows {
|
||||
let sector = row.sector.as_deref().unwrap_or("(unknown)");
|
||||
let hop = row
|
||||
.hop
|
||||
.map(|h| h.to_string())
|
||||
.unwrap_or_else(|| "?".to_string());
|
||||
if sector != last_sector {
|
||||
if !last_sector.is_empty() {
|
||||
println!();
|
||||
}
|
||||
last_sector = sector.to_string();
|
||||
}
|
||||
println!("{:<30} {:>4} {:>9}", sector, hop, row.remaining);
|
||||
|
||||
#[derive(Serialize)]
|
||||
struct CorridorResult {
|
||||
total: i64,
|
||||
corridors: Vec<CorridorRow>,
|
||||
}
|
||||
println!("{}", "-".repeat(48));
|
||||
println!("{:<30} {:>4} {:>9}", "TOTAL", "", total);
|
||||
println!(
|
||||
"{}",
|
||||
serde_json::to_string_pretty(&CorridorResult {
|
||||
total,
|
||||
corridors: rows,
|
||||
})
|
||||
.unwrap()
|
||||
);
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
Reference in New Issue
Block a user