Files
settled-reach/tooling/planet-gen/apply_name_fixes.py
T
jpmschweitzerandClaude Sonnet 4.6 dd50287af0 feat(content): atlas cohesion analysis tooling for #849
Adds two reusable scripts for the core-world hand-refine pass:

- atlas_cohesion_audit.py: SQL analysis against atlas_* tables. Reports
  empty names, lazy/generic outputs, cardinal direction density, earth-echo
  concentration, same-body cross-feature stem duplicates, and cross-body
  stem collisions within a system. Supports --system, --body, --db flags.
  Baseline run ranked Ran and Tau Ceti as highest-priority targets.

- apply_name_fixes.py: Applies curated name replacement tables to
  markers.json files (name fields only; geometry preserved). Supports
  --dry-run. After running, caller syncs DB via generate_atlas.py --body.

- refine_log_849.md: Hand-refine log documenting each body touched, the
  rationale per change, cross-reference arcs established, and systems
  flagged as blocked or needing follow-up (Sol, Barnard's Star, Proxima).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-19 12:34:47 +02:00

188 lines
7.5 KiB
Python

#!/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 <id> 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
}
# ---------------------------------------------------------------------------
# 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()