#!/usr/bin/env python3 """ apply_name_fixes.py — Apply curated name replacements to markers.json files. Usage: python3 tooling/planet-gen/apply_name_fixes.py [--dry-run] Edits name fields only; all geometry (path, center, area_fraction) is preserved. After running, call generate_atlas.py --body for each body to sync DB. Decisions: D-191 (markers.json format, corridor palettes) """ import json import sys from pathlib import Path REPO_ROOT = (Path(__file__).resolve().parent / ".." / "..").resolve() WIKI = REPO_ROOT / "wiki" / "star-systems" DRY_RUN = "--dry-run" in sys.argv # --------------------------------------------------------------------------- # Name replacement tables per body # Format: { body_dir_key: { "feature_type": { "old_name": "new_name" } } } # feature_type: cities | rivers | oceans | mountain_ranges | pois # --------------------------------------------------------------------------- FIXES: dict[str, dict[str, dict[str, str]]] = { # ----------------------------------------------------------------------- # GJ 144 — Ran system # Verified against actual markers.json files (DB is stale). # ----------------------------------------------------------------------- # Kallast (GJ144d) — 2B pop, agricultural, urban_concentrated, ocean world # Current state (from markers.json): good Nordic naming pass done; residual # lazy/generic oceans remain. Fix: rename remaining generics, add cross-ref # with mountain "Seterfjellet" (ocean → "Seterfjord"), fix POI, and rename # "Aldren Pass" to avoid cross-system collision with Lendel (GJ380c capital # "Aldren" and river "The Aldren"). # Narrative hook: "Rán's Landing" city (names the star), "Rán's Run" and # "Ranfall Beck" rivers (shared Rán stem = cross-referencing done). Adding # Seterfjord → Seterfjellet link completes the second cross-reference arc. "GJ-144/bodies/GJ144d": { "rivers": { # Avoid cross-system stem collision with Lendel (GJ380c) "Aldren" "Aldren Pass": "Randalfoss", }, "oceans": { # Still generic — large ocean near mountains → name ties to Seterfjellet "Boulder Bluff": "Keldmere", # "High Mesa" for an ocean is nonsensical "High Mesa": "Seterfjord", }, "pois": { "Summit Point": "Kallast Gate Terminal", }, }, # Vethis (GJ144e) — 1.2B pop, agricultural, dispersed_rural # Current state: partial naming pass done. Remaining: cardinals (West Bend), # earth-echoes (Blue Ridge), generics (Stone Point, Ironside Run, Golden # Valley, Broad Fork), "Ash" stem overuse (Ashbluff Sea + Ashstone Ridge + # Ashvale = 3 features), "Iron" stem overuse (Irongate + Ironside Run = 2). # Fixes cross-reference existing named features: # Grey- stem: Greywash + Greywash Fork + Greystone Ridge (intentional arc) # Thorn- stem: Thorncrests + Thornrun (mountain visible from river) # Kel- stem: Kelbridge + Kelside Run (river serving the city) # Veth- stem: Vethis + Veth Delta + Veth Mere (planet name echoed in geography) "GJ-144/bodies/GJ144e": { "rivers": { "West Bend": "Greywash Fork", # cardinal → cross-refs Greywash river "Blue Ridge": "Ashvale Beck", # earth-echo → cross-refs Ashvale city "Stone Point": "Thornrun", # generic → cross-refs Thorncrests mtn "Ironside Run": "Kelside Run", # Iron overuse → cross-refs Kelbridge }, "oceans": { # Ashbluff Sea: "Ash" prefix already on Ashvale city + Ashstone Ridge mtn "Ashbluff Sea": "Veth Mere", # Ash overuse → cross-refs Veth Delta }, "mountain_ranges": { "Golden Valley": "Greymoor Range", # generic → cross-refs Greywash river "Broad Fork": "Keld Spur", # generic → Nordic "keld" (spring) # Ashstone Ridge: "Ash" overuse (Ashvale + Veth Mere rename frees slot) "Ashstone Ridge": "Greystone Ridge", # Ash overuse → cross-refs Greywash }, "pois": { "Ridge Crossing": "Vethis Gate Terminal", }, }, # ----------------------------------------------------------------------- # GJ 71 — Tau Ceti system # Verified against actual markers.json files (DB is stale). # ----------------------------------------------------------------------- # Threshold (GJ71c) — 600M pop, agricultural, urban_concentrated # Fix: replace "Aethelred" (Anglo-Saxon; breaks the all-Latin-personal-name # pattern of the six rivers: Octavius, Septimus, Quintus, Valeria, Marcus). # Narrative: All six rivers named after the original Commission survey team. "GJ-71/bodies/GJ71c": { "rivers": { "Aethelred": "Gaius", }, }, # Arden (GJ71d) — 500M pop, agricultural, dispersed_rural # Fix cross-body stem duplicates: # "Concordia Hall" city → exact stem match with GJ71c ocean "Concordia" # "Basilica Nova" river → exact name match with GJ71c POI "Basilica Nova" # GJ71d-1 has been independently updated — Forum Major no longer conflicts. # Capital "Palaestra" and other names are clean — keep. "GJ-71/bodies/GJ71d": { "cities": { "Concordia Hall": "The Praxis", # Concordia = GJ71c sea }, "rivers": { "Basilica Nova": "Via Principia", # exact match = GJ71c POI name }, }, # Verantis (GJ71d-1) — 20M pop, transit moon of Arden # Already updated in a prior pass (capital=Praetorium, mountains all # renamed to unique Latin institutional names). No changes needed. # "GJ-71/bodies/GJ71d-1": {}, # skip # ----------------------------------------------------------------------- # GJ 244A — Sirius system # Verified against actual markers.json files (DB matches). # Note: "Westwall" flagged by Gestalt does NOT appear in Edict markers.json # or DB — either already removed in a prior pass or Gestalt saw stale data. # ----------------------------------------------------------------------- # Edict (GJ244Ad) — 400M pop, hand-authored template + #833 derived features # Hand-authored core is clean (Mandate, Station Edict, Founder's Range, # Accord Peaks, Veto Spur, Concord Assembly Archive, all POIs). Fixes # target only #833-generated features that don't match the political-legal # naming vocabulary of Edict. # Narrative hook: the legal/institutional vocabulary — Mandate, Accord, Veto, # Concord, Statute, Charter — all reference governance and founding documents. # Arcs established: Concord- (ocean + POI), Charter- (mountain + ocean), # Veil- (ocean + POI cross-ref to Veil Institute Survey Station). "GJ-244A/bodies/GJ244Ad": { "mountain_ranges": { # "Keel" is a nautical term — wrong vocabulary for a political world "Keel Ridge": "Charter Spur", # charter = Edict's founding document # "Sanction" fits but "Ridge" suffix is lazy "Sanction Ridge": "The Statute", # distinctive; drops generic suffix }, "oceans": { # Cardinal direction names; replace with Edict-vocabulary terms "Upper Shelf": "Veil Shelf", # cross-refs Veil Institute Survey Station "Southern Melt": "Concord Mere", # cross-refs Concord Assembly Archive POI "Eastern Tarn": "Charter Tarn", # cross-refs mountain "Charter Spur" }, }, # ----------------------------------------------------------------------- # GJ 380 — Groombridge system # Verified against actual markers.json files. # Hand-authored template is clean: Aldren (capital) + The Aldren (river) + # Aldren Exchange (POI) is an INTENTIONAL arc — do not touch. # "Meridian Risk HQ" POI is a corporation proper noun — not geography. # ----------------------------------------------------------------------- # Lendel (GJ380c) — 900M pop, hand-authored template + #833 derived features # Two #833-generated features with lazy suffixes. British-Isles naming # vocabulary: Lendel, Aldren, Sethvale, Caldenmere, Rethain Sea. # "Durneth Beck" cross-refs Durneth Range (the major mountain range near # the river's source area). "Tember Spine" keeps the "Tember" stem but # drops the lazy "Ridge" suffix. "GJ-380/bodies/GJ380c": { "rivers": { "Pale Run": "Durneth Beck", # lazy → cross-refs Durneth Range; beck=British stream }, "mountain_ranges": { "Tember Ridge": "Tember Spine", # lazy Ridge → Spine (distinctive; not in lazy list) }, }, # ----------------------------------------------------------------------- # GJ 699 — Barnard's Star system # Verified against actual markers.json files. # ----------------------------------------------------------------------- # Verada (GJ699b) — 1.9B pop, hop-0, dense urban world # Major problem: rivers and ocean names use urban civic vocabulary # (Grandview Square, Meridian City, Metropolitan Square) — these are # neighborhood-planning names, not geographic features. Rivers especially # cannot be named "Grandview Square" or "Meridian City". Oceans have # district-style names that duplicate on sibling bodies. # Strategy: keep Verada's civic-density character in CITY names, but # give geographic features geographic names that cross-reference the cities. # Arcs: Capitol- (city + river + ocean), Haven/Port- (city + ocean), # Meridian- (river + ocean), Prospect- (ocean), Sterling- (lake). "GJ-699/bodies/GJ699b": { "rivers": { # "Grandview Square" is a plaza, not a river name "Grandview Square": "Verada Reach", # planet name + geographic; Reach = stretch of river # "Meridian City" is a city name used for a river "Meridian City": "The Meridian", # drop civic suffix; becomes proper river name # "Summit View" is lazy "Summit View": "Capitol Beck", # cross-refs Capitol Heights capital city }, "oceans": { # "Terrace" is an architectural term — wrong for a large ocean "Prospect Terrace": "Prospect Sea", # keep Prospect, fix suffix # "Metropolitan Square" is a plaza name for an ocean "Metropolitan Square": "Haven Sea", # cross-refs Port Haven city # Exact dup of GJ699b-1 mountain "Central District" "Central District": "Meridian Sound", # cross-refs The Meridian river # "Plaza" is civic architecture, not a lake feature "Liberty Plaza": "Capitol Mere", # cross-refs Capitol Heights capital # "Sterling Heights" — "Heights" is wrong for a lake; dupes GJ699b-1 mountain "Sterling Heights": "Sterling Pool", # keep Sterling, fix suffix }, }, # Verada's moon (GJ699b-1) — uninhabited transit moon # All 8 mountains are named after urban street addresses (Grandview Avenue, # Harmony Boulevard, Beacon Street, etc.) — this is entirely wrong for # mountain ranges. The fix renames them to geological/geographic terms that # cross-reference Verada's institutional vocabulary (as if settlers looked # down at Verada and named the moon's peaks after the institutions below). # The largest range (Verada Scarp, was "Grandview Avenue") takes the planet # name directly. "GJ-699/bodies/GJ699b-1": { "mountain_ranges": { "Grandview Avenue": "Verada Scarp", # largest range; named after parent body "Central District": "Barnard Heights", # names the star; distinct from all Verada names "Harmony Boulevard": "Keystone Scarp", # cross-refs Keystone Junction on Verada "Capitol Heights": "Tribunal Spur", # cross-refs Tribunal Tower on Verada "Meridian Plaza": "Zenith Spine", # cross-refs Zenith Station on Verada "Liberty Square": "Ironwood Spur", # cross-refs Ironwood Borough on Verada "Metropolitan Way": "Consulate Scarp", # cross-refs Consulate Way on Verada "Beacon Street": "Prefecture Spur", # cross-refs Prefecture Hall on Verada }, }, } # --------------------------------------------------------------------------- # Helpers # --------------------------------------------------------------------------- def apply_fixes(body_key: str, fixes: dict[str, dict[str, str]]) -> bool: """Load markers.json for body_key, apply name fixes, write back. Returns True on change.""" parts = body_key.split("/") path = WIKI / Path(*parts) / "markers.json" if not path.exists(): print(f" SKIP {body_key}: markers.json not found at {path}") return False with open(path) as f: markers = json.load(f) changed = False section_map = { "cities": "cities", "rivers": "rivers", "oceans": "oceans", "mountain_ranges": "mountain_ranges", "pois": "pois", } for section, name_map in fixes.items(): feature_list = markers.get(section_map[section], []) for feature in feature_list: old_name = feature.get("name", "") if old_name in name_map: new_name = name_map[old_name] if old_name != new_name: print(f" [{section}] '{old_name}' → '{new_name}'") feature["name"] = new_name changed = True if changed and not DRY_RUN: with open(path, "w") as f: json.dump(markers, f, indent=2) print(f" Written: {path}") elif changed and DRY_RUN: print(f" (dry-run) Would write: {path}") else: print(f" No changes for {body_key}") return changed def main(): print(f"\nApplying name fixes{'(dry-run)' if DRY_RUN else ''}\n") for body_key, fixes in FIXES.items(): print(f"=== {body_key} ===") apply_fixes(body_key, fixes) print() print("Done.\n") if __name__ == "__main__": main()