data(atlas): author hop-0 and hop-1 body catalogs (6 systems)

Systems authored: Sol (GJ 0), Gateway (GJ 71), Ran (GJ 144),
Sirius (GJ 244A), ACB (GJ 559B), Struve (GJ 725B).

Total: 59 bodies + 10 stations across 6 systems.

Named bodies: Mercury, Venus, Earth, Luna, Mars, Phobos, Deimos,
Jupiter, Io, Europa, Ganymede, Callisto, Saturn, Titan, Enceladus,
Uranus, Neptune (Sol); Threshold, Arden, Verantis (Gateway);
Kallast, Vethis (Ran); Edict (Sirius); Sede (ACB); Rush (Struve).
Named stations: Kallast Freight Terminal, Conclave, Aperture,
Leverage, Quorum, The Stack.

Atlas CLI: added commit-system auto-update of star_systems counts,
next command for hop-ordered authoring, sync-wiki with full field
rendering. Edict naming lore added to Sirius wiki by Miri.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-03-24 19:04:10 +01:00
co-authored by Claude Opus 4.6
parent c970fac1f9
commit c9d3bd4eed
10 changed files with 1073 additions and 2 deletions
+138
View File
@@ -147,6 +147,11 @@ enum Commands {
},
/// List systems that have no bodies yet
Unfinished,
/// List unfinished systems at a specific hop distance (or next available hop)
Next {
/// Hop distance (omit to find the lowest hop with unfinished systems)
hop: Option<i32>,
},
/// Sync body/station data into wiki page for a system (or all systems)
SyncWiki {
/// System ID (omit for all systems with bodies)
@@ -1187,6 +1192,51 @@ fn cmd_commit_system(conn: &Connection, path: &str) {
});
}
// Update star_systems counts from the actual body data
let habitable_count: i32 = proposal
.bodies
.iter()
.filter(|b| {
b.atmosphere.as_deref() == Some("breathable")
&& (b.body_type == "planet" || b.body_type == "moon")
})
.count() as i32;
let inhabited_count: i32 = proposal
.bodies
.iter()
.filter(|b| b.inhabited)
.count() as i32
+ proposal.stations.iter().filter(|s| {
s.population.unwrap_or(0) > 0 || s.station_type == "horizon"
}).count() as i32;
let has_gas_giant: i32 = proposal
.bodies
.iter()
.any(|b| b.body_type == "gas_giant")
as i32;
let has_belt: i32 = proposal
.bodies
.iter()
.any(|b| b.body_type == "asteroid_belt")
as i32;
tx.execute(
"UPDATE star_systems SET habitable_planet_count = ?1, inhabited_planet_count = ?2,
gas_giant = ?3, asteroid_belt = ?4
WHERE system_id = ?5",
params![
habitable_count,
inhabited_count,
has_gas_giant,
has_belt,
proposal.system_id,
],
)
.unwrap();
tx.commit().unwrap();
#[derive(Serialize)]
@@ -1194,11 +1244,15 @@ fn cmd_commit_system(conn: &Connection, path: &str) {
system_id: String,
bodies_created: usize,
stations_created: usize,
habitable_count: i32,
inhabited_count: i32,
}
let result = CommitResult {
system_id: proposal.system_id,
bodies_created: proposal.bodies.len(),
stations_created: proposal.stations.len(),
habitable_count,
inhabited_count,
};
println!("{}", serde_json::to_string_pretty(&result).unwrap());
}
@@ -1272,6 +1326,89 @@ fn cmd_unfinished(conn: &Connection) {
println!("{}", serde_json::to_string_pretty(&result).unwrap());
}
fn cmd_next(conn: &Connection, hop: Option<i32>) {
// Find the target hop — either specified or the lowest with unfinished systems
let target_hop: i32 = if let Some(h) = hop {
h
} else {
conn.query_row(
"SELECT MIN(g.hop_distance_from_gateway)
FROM star_systems s
JOIN system_gates g ON s.system_id = g.system_id
WHERE s.system_id NOT IN (SELECT DISTINCT system_id FROM bodies)",
[],
|r| r.get::<_, Option<i32>>(0),
)
.unwrap()
.unwrap_or(-1)
};
if target_hop < 0 {
println!(r#"{{"hop": null, "count": 0, "systems": [], "message": "all systems have bodies"}}"#);
return;
}
#[derive(Serialize)]
struct NextSystem {
system_id: String,
proper_name: Option<String>,
star_type: Option<String>,
spectral_class: Option<String>,
gate_topology: Option<String>,
geographic_sector: Option<String>,
habitable_planet_count: Option<i32>,
inhabited_planet_count: Option<i32>,
population: Option<i64>,
}
let mut stmt = conn
.prepare(
"SELECT s.system_id, s.proper_name, s.star_type, s.spectral_class,
g.gate_topology, s.geographic_sector,
s.habitable_planet_count, s.inhabited_planet_count,
e.population
FROM star_systems s
JOIN system_gates g ON s.system_id = g.system_id
LEFT JOIN system_economy e ON s.system_id = e.system_id
WHERE g.hop_distance_from_gateway = ?1
AND s.system_id NOT IN (SELECT DISTINCT system_id FROM bodies)
ORDER BY s.system_id",
)
.unwrap();
let systems: Vec<NextSystem> = stmt
.query_map(params![target_hop], |row| {
Ok(NextSystem {
system_id: row.get(0)?,
proper_name: row.get(1)?,
star_type: row.get(2)?,
spectral_class: row.get(3)?,
gate_topology: row.get(4)?,
geographic_sector: row.get(5)?,
habitable_planet_count: row.get(6)?,
inhabited_planet_count: row.get(7)?,
population: row.get(8)?,
})
})
.unwrap()
.filter_map(|r| r.ok())
.collect();
#[derive(Serialize)]
struct NextResult {
hop: i32,
count: usize,
systems: Vec<NextSystem>,
}
let result = NextResult {
hop: target_hop,
count: systems.len(),
systems,
};
println!("{}", serde_json::to_string_pretty(&result).unwrap());
}
fn cmd_sync_wiki(conn: &Connection, system_id: Option<&str>, wiki_dir: &str) {
// Get list of systems to sync
let system_ids: Vec<String> = if let Some(sid) = system_id {
@@ -1609,6 +1746,7 @@ fn main() {
Commands::CommitSystem { path } => cmd_commit_system(&conn, path),
Commands::WipeSystem { system_id } => cmd_wipe_system(&conn, system_id),
Commands::Unfinished => cmd_unfinished(&conn),
Commands::Next { hop } => cmd_next(&conn, *hop),
Commands::SyncWiki { system_id, wiki } => {
cmd_sync_wiki(&conn, system_id.as_deref(), wiki)
}