Files
settled-reach/tooling/test_planet_determinism.py
T
jpmschweitzerandClaude Opus 5.5 668772075c refactor(tooling): T-1288 — planet-gen becomes reach atlas planet
The 30-file tree moves under atlas as its third rung (D-243), ten verbs
fronting it. Each verb restates its module's options so `--help` describes
something; tooling/test_planet_router.py hands every declared option to the
module's own argparse and fails on drift, and now runs in make test-tooling.

The 2026-09-02 half of this move had converted the top-level imports and the
repo roots. Finishing it found what the half-move left:

- Lazy in-function imports, and all of sol_data/, still named siblings bare.
  They resolved only through sys.path.insert hacks, so under reach the first
  globe render in generate, batch or sol-import would have raised
  ModuleNotFoundError. Qualified; the hacks are gone.
- 247 print() calls and a stdout progress writer that fired once per 8 KB
  block. Report verbs (audit, quality) write through console.out, progress
  through console.event, and download progress is throttled to 10% steps
  so a job log is not tens of thousands of lines.
- Every error exit raises ReachError with a fix.

Two checks that could not fail:

- batch --verify-determinism printed a warning and exited 0 on a mismatch.
- import-provinces exited 0 with errors > 0.

Both now raise. The 271-body bake is only safe to re-run because the first
one holds.

sol-import --body is action="append" in the module but the router took one
value, so --body GJ0d --body GJ0e kept one. Now repeatable, and _flags repeats
list options.

test_conformance walked one level, so a nested group was reported as a verb
missing @command and its ten verbs were never checked. It recurses now;
proven by stripping @command from `planet quality` and watching it fail.

Stray PNGs from the 2026-09-03 runaway router-test run are parked in
.cache/t1288-stray-pngs/, not committed. Their reliefmaps differ from HEAD
while the heightmap regenerated byte-identical — filed as T-1291.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-23 16:08:02 +02:00

72 lines
2.4 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#!/usr/bin/env python3
"""Determinism guard for planet_simulation.simulate() (#963).
The 1024×512 elevation bump (D-202 amended) claims to be deterministic — the
271-body heightmap bake cannot be re-run cheaply, so a silent non-determinism
means a full regeneration. This asserts that simulating the same body twice
yields a bit-identical elevation array.
Run: .venv/bin/python tooling/test_planet_determinism.py
Exit: 0 = deterministic, 1 = drift detected, 2 = could not find a test body.
"""
import hashlib
import sqlite3
import sys
from pathlib import Path
REPO = Path(__file__).resolve().parent.parent
sys.path.insert(0, str(REPO))
import numpy as np # noqa: E402
from tooling.domains.atlas.planet.body_definition_parser import parse_system # noqa: E402
from tooling.domains.atlas.planet.planet_simulation import GRID_H, GRID_W, simulate # noqa: E402
def _elev_hash(terrain) -> str:
arr = np.ascontiguousarray(terrain["elevation"], dtype=np.float32)
return hashlib.sha256(arr.tobytes()).hexdigest()
def main() -> int:
conn = sqlite3.connect(str(REPO / "server" / "data" / "systems.db"))
rows = conn.execute(
"SELECT body_id, terrain_reference FROM bodies "
"WHERE inhabited=1 AND terrain_reference IS NOT NULL AND population>0 "
"AND system_id != 'GJ 0' ORDER BY body_id LIMIT 30"
).fetchall()
checked = 0
for body_id, tref in rows:
if checked >= 3:
break
sys_index = tref.split("/bodies/")[0] + "/index.md"
try:
defs = parse_system(sys_index)
except Exception:
continue
bd = next((d for d in defs if d.get("id") == body_id), None)
if bd is None:
continue
t1 = simulate(bd)
t2 = simulate(bd)
if not t1 or "elevation" not in t1:
continue
h1, h2 = _elev_hash(t1), _elev_hash(t2)
ok = h1 == h2 and t1["elevation"].shape == (GRID_H, GRID_W)
print(f" {body_id}: {'OK' if ok else 'DRIFT'} {h1[:16]} shape={t1['elevation'].shape}")
if not ok:
print(f"FAIL: {body_id} elevation is non-deterministic ({h1[:16]} != {h2[:16]})")
return 1
checked += 1
if checked == 0:
print("SKIP: no testable body found")
return 2
print(f"PASS: {checked} bodies simulate deterministically at {GRID_W}×{GRID_H}")
return 0
if __name__ == "__main__":
sys.exit(main())