Files
settled-reach/tooling/process-wiki-system-changes
T
jpmschweitzerandClaude Opus 4.6 cb6d14ced4 feat(db): add wiki sync pipeline and process-wiki-system-changes
New data flow: wiki → star-map.json + SQLite.
- tooling/process-wiki-system-changes: unified script that reads
  wiki, updates star-map.json topology, syncs to SQLite
- tooling/db/wiki_sync.py: wiki frontmatter parser + SQLite upsert
- generate-stubs.py: updated for full-frontmatter wiki format
- Removed extract-csv.py (replaced by SQLite queries)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-14 00:23:59 +01:00

202 lines
6.2 KiB
Python
Executable File

#!/usr/bin/env python3
"""
process-wiki-system-changes
Propagates wiki changes to star-map.json and SQLite.
Wiki pages (wiki/star-systems/*/index.md) are the single source of truth.
This script:
1. Reads all wiki frontmatter
2. Updates star-map.json nodes (preserving edges/topology)
3. Syncs all data into SQLite star_systems table
4. Reports completion stats
Usage:
tooling/process-wiki-system-changes Run full sync
tooling/process-wiki-system-changes --dry-run Show what would change
tooling/process-wiki-system-changes --stats Just show fill rates
"""
import argparse
import json
import sys
from pathlib import Path
SCRIPT_DIR = Path(__file__).resolve().parent
WORKTREE_ROOT = (SCRIPT_DIR / "..").resolve()
STAR_MAP_PATH = WORKTREE_ROOT / "docs" / "design" / "star-map.json"
# Import wiki_sync for DB operations
sys.path.insert(0, str(SCRIPT_DIR / "db"))
import wiki_sync
# ---------------------------------------------------------------------------
# Star-map.json update
# ---------------------------------------------------------------------------
# Fields that star-map.json nodes carry (topology + join keys)
STAR_MAP_NODE_FIELDS = [
"system_id", "astronomical_id",
"gate_topology", "aperture_count", "gate_connections",
"hop_distance_from_gateway",
]
def update_star_map(wiki_systems: dict) -> dict:
"""Update star-map.json node fields from wiki data. Returns summary."""
with open(STAR_MAP_PATH, "r") as f:
data = json.load(f)
nodes = data["nodes"]
updated = 0
added = 0
wiki_sids = set(wiki_systems.keys())
map_sids = {n["system_id"] for n in nodes}
# Update existing nodes
for node in nodes:
sid = node["system_id"]
if sid not in wiki_systems:
continue
wiki = wiki_systems[sid]
changed = False
for field in STAR_MAP_NODE_FIELDS:
if field in wiki and wiki[field] != "":
val = wiki[field]
# Coerce integers
if field in ("aperture_count", "gate_connections", "hop_distance_from_gateway"):
try:
val = int(val)
except (ValueError, TypeError):
continue
if node.get(field) != val:
node[field] = val
changed = True
# Preserve is_gateway flag
if changed:
updated += 1
# Add nodes for wiki pages that aren't in star-map yet
for sid in wiki_sids - map_sids:
wiki = wiki_systems[sid]
new_node = {"system_id": sid}
for field in STAR_MAP_NODE_FIELDS:
if field in wiki and wiki[field] != "":
val = wiki[field]
if field in ("aperture_count", "gate_connections", "hop_distance_from_gateway"):
try:
val = int(val)
except (ValueError, TypeError):
continue
new_node[field] = val
nodes.append(new_node)
added += 1
with open(STAR_MAP_PATH, "w") as f:
json.dump(data, f, indent=2)
f.write("\n")
return {
"nodes": len(nodes),
"edges": len(data["edges"]),
"updated": updated,
"added": added,
}
def dry_run(wiki_systems: dict) -> dict:
"""Show what would change in star-map.json without writing."""
with open(STAR_MAP_PATH, "r") as f:
data = json.load(f)
changes = []
for node in data["nodes"]:
sid = node["system_id"]
if sid not in wiki_systems:
continue
wiki = wiki_systems[sid]
for field in STAR_MAP_NODE_FIELDS:
if field in wiki and wiki[field] != "":
val = wiki[field]
if field in ("aperture_count", "gate_connections", "hop_distance_from_gateway"):
try:
val = int(val)
except (ValueError, TypeError):
continue
if node.get(field) != val:
changes.append(f" {sid}.{field}: {node.get(field)} -> {val}")
return {"changes": changes}
# ---------------------------------------------------------------------------
# Main
# ---------------------------------------------------------------------------
def main():
parser = argparse.ArgumentParser(
description="Propagate wiki changes to star-map.json and SQLite"
)
parser.add_argument("--dry-run", action="store_true", help="Show changes without writing")
parser.add_argument("--stats", action="store_true", help="Just show fill rates")
args = parser.parse_args()
cfg = wiki_sync.load_config()
if args.stats:
wiki_sync.stats(cfg)
return
# Load wiki
wiki_systems, warnings, body_count = wiki_sync.load_wiki_pages()
if not wiki_systems:
print("ERROR: No wiki pages found")
for w in warnings:
print(f" {w}")
sys.exit(1)
print(f"Loaded {len(wiki_systems)} wiki pages ({body_count} body sections filled)")
if warnings:
print(f"Warnings: {len(warnings)}")
for w in warnings[:5]:
print(f" {w}")
if len(warnings) > 5:
print(f" ... and {len(warnings) - 5} more")
if args.dry_run:
result = dry_run(wiki_systems)
if result["changes"]:
print(f"\nstar-map.json changes ({len(result['changes'])}):")
for c in result["changes"]:
print(c)
else:
print("\nstar-map.json: no changes")
print("\n(dry run — nothing written)")
return
# Step 1: Update star-map.json
print("\n--- Updating star-map.json ---")
map_result = update_star_map(wiki_systems)
print(f"Nodes: {map_result['nodes']}, Edges: {map_result['edges']}")
print(f"Updated: {map_result['updated']}, Added: {map_result['added']}")
# Step 2: Sync to SQLite
print("\n--- Syncing to SQLite ---")
db_result = wiki_sync.sync(cfg)
if db_result["ok"]:
print(db_result["summary"])
else:
print(f"ERROR: {db_result.get('error')}")
sys.exit(1)
# Step 3: Quick stats
print("\n--- Completion ---")
wiki_sync.stats(cfg)
if __name__ == "__main__":
main()