Files
jpmschweitzerandClaude Opus 5 b1b57d603f feat(config): T-1285 — the atlas authoring verbs, and a verify nobody checked
reach atlas db / names / systems-done / check / verify / commit-and-sync /
update-field / flatness. Eight scripts retired, five of them bash. verify
reproduces the original exactly: 2 errors across 301 proposals, exit 1.

The binary has more verbs than its wrapper documented. The bash usage text
listed four; atlas-commit-and-sync calls four more it never mentioned. All
eight are declared so reach atlas --help is a complete index, and unknown verbs
are still forwarded — a hand-maintained list falls behind the binary it
describes, so rejecting on it would break the day someone adds a subcommand.

commit-and-sync now stages by default and commits only with --commit. Nothing
else in reach writes to git history, and committing as a side effect of "sync"
is a different risk class from writing a file; the default prints the message
it would use, leaving the decision where it was.

Two real bugs found in that script while porting it. It ran atlas-verify and
never checked the exit code, so a proposal that FAILED verification was still
wiped, committed and synced — bad data in systems.db is far harder to undo than
a failed command, and it now refuses. And it hardcoded a pinned
"Co-Authored-By: Claude Opus 4.6" into every atlas commit, which the git-commit
skill names as the root cause of attribution drift.

update-field gains two guards the original lacked. Its field→table map lived
inside a bash heredoc string where nothing could check it, and an unknown field
produced an UPDATE against a table of None; it now names the nine accepted
fields. And it checks rowcount, so a system_id that does not exist is a failure
rather than a silent no-op reported as success.

The three Python scripts moved with the usual treatment — prints to console
events, argparse replaced by typed functions, __file__ roots to
config.repo_root(). No root bug this time: checked before moving rather than
after, three domains running.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-02 14:36:51 +02:00

74 lines
2.7 KiB
Python
Executable File

#!/usr/bin/env python3
"""Quick diagnostic for an atlas proposal — shows planet count, body summary, and flags.
Usage:
tooling/atlas-check docs/atlas/proposals/GJ1075.json
"""
import json
from pathlib import Path
from tooling.core import console
def check(path: Path):
with open(path) as f:
p = json.load(f)
bodies = p.get("bodies", [])
stations = p.get("stations", [])
planets = [b for b in bodies if b["body_type"] == "planet" and not b.get("parent_body_id")]
gas_giants = [b for b in bodies if b["body_type"] == "gas_giant"]
moons = [b for b in bodies if b["body_type"] == "moon"]
belts = [b for b in bodies if b["body_type"] == "asteroid_belt"]
oort = [b for b in bodies if b["body_type"] == "oort_cloud"]
sc = p.get("spectral_class", "")
if sc.startswith(("G", "F")):
min_p = 8
elif sc.startswith("K"):
min_p = 7
else:
min_p = 6
console.event(f"=== {path.name}{p.get('proper_name') or p['system_id']} ===")
console.event(f" star_type: {p.get('star_type')} spectral: {sc}")
console.event(f" planets: {len(planets)} (need {min_p}+) gas_giants: {len(gas_giants)} moons: {len(moons)}")
console.event(f" belts: {len(belts)} oort: {len(oort)} stations: {len(stations)}")
if planets:
console.event(f" orbits: {' → '.join(str(b['orbit_index']) for b in planets)}")
console.event(f" gravity: {' / '.join(str(b['surface_gravity']) for b in planets)}")
console.event(f" periods: {' / '.join(str(b['orbital_period_days']) for b in planets)} days")
inhabited = [b for b in bodies if b.get("inhabited")]
if inhabited:
for b in inhabited:
console.event(f" inhabited: {b['body_id']} ({b.get('proper_name', '?')}) pop={b.get('population')}")
for s in stations:
console.event(f" station: {s['station_id']} ({s.get('proper_name', '?')}) type={s['station_type']} pop={s.get('population')}")
# Flags
if len(planets) < min_p:
console.event(f" ⚠ need {min_p - len(planets)} more planet(s)")
if not belts:
console.event(" ⚠ no asteroid belt")
if not oort:
console.event(" ⚠ no oort cloud")
horizons = [s for s in stations if s["station_type"] == "horizon"]
if not horizons:
console.event(" ⚠ no horizon station")
elif not any(s.get("has_gate_infrastructure") for s in horizons):
console.event(" ⚠ horizon station missing gate_infrastructure")
def report(proposals: list[Path]) -> None:
"""Run the diagnostic over each proposal. Purely informational — no verdict.
The original's arg handling lived in a bare sys.argv loop; the router owns
that now.
"""
for path in proposals:
check(path)