feat(simulation): feature-name pipeline wired + legacy window_granularity u32 retired (T-1169, T-1159)
One commit for two tickets whose changes share the bridge/plugin plumbing files. T-1169 connects the three dormant feature-name pieces: atlas_feature_names populated at regen (17,891 rows — 15,190 mountain, 2,701 river — via populate_atlas_feature_names mirroring the city-names importer; systems.db regenerated, stamp fresh), attach_feature_names wired into the cascade's Topography block with name pools threaded DB-free through AnalyzeBody (D-225 pattern) and assignments stored on Layer1Output/BodyWorldState for future consumers, and a FeatureNamesRequest/Response read proxy as the bridge's 7th tagged envelope (D-236 pattern, both SimBridge impls). Client label DRAW is deliberately NOT here — implementation proved both river and mountain labels need a wire-carried position (the pool is position-free; course polylines aren't correlated with the named attractors by construction) — deferred to T-1195's single design pass. cascade_layer1 golden re-pinned (additive feature_names field). T-1159 retires the legacy u32 granularity field fully shadowed by window_granularity_v2: AtlasLayerRequest.window_granularity, DistrictWindowLayer.granularity echo, the u32::MAX sentinel, and resolve_window_granularity are gone server-side; client encode paths and the caller-less atlas_window_cache legacy key component dropped; msgpack fixtures regenerated; the T-1150 aliasing regression test now drives through the surviving enum field. The district_window carrier itself survives byte-compatible per D-255(c). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -164,6 +164,93 @@ def populate_atlas_city_names(conn: sqlite3.Connection, dry_run: bool) -> int:
|
||||
return len(rows)
|
||||
|
||||
|
||||
# markers.json `names` key -> atlas_feature_names.feature_type value. Only
|
||||
# rivers/mountain_ranges are populated here (T-1169 scope — the two pools
|
||||
# server/src/atlas/layer1.rs's `attach_feature_names` actually consumes,
|
||||
# river_names/mountain_names). `oceans`/`passes`/`regions` pools are NOT
|
||||
# wired to any production consumer yet (no `attach_feature_names`-equivalent
|
||||
# reads them) — populating rows for them here would be dead data with
|
||||
# nothing to keep it honest, the same D-223 discipline that keeps
|
||||
# atlas_city_names to names an actual placement pipeline consumes.
|
||||
_FEATURE_NAME_POOL_KEYS: dict[str, str] = {
|
||||
"rivers": "river",
|
||||
"mountain_ranges": "mountain",
|
||||
}
|
||||
|
||||
|
||||
def populate_atlas_feature_names(conn: sqlite3.Connection, dry_run: bool) -> int:
|
||||
"""Populate atlas_feature_names from the names-only markers.json pools
|
||||
(D-223, T-1169) — mirrors `populate_atlas_city_names` exactly, over the
|
||||
`rivers`/`mountain_ranges` marker keys instead of `cities`:
|
||||
- body_id : directory name (e.g. GJ0e)
|
||||
- name : pooled feature name
|
||||
- feature_type : 'river' | 'mountain' (see `_FEATURE_NAME_POOL_KEYS`)
|
||||
- priority : 0 — no authored ranking signal in markers.json; the
|
||||
server-side name attachment (`layer1::attach_feature_names`)
|
||||
ranks by computed mouth/peak strength at assignment time,
|
||||
not by a priority stored on the pool row.
|
||||
|
||||
markers.json is a names-only flavoured pool (D-223): it carries no
|
||||
geometry or position. `attach_feature_names` (Phase 4 cascade) attaches
|
||||
these names to computed river mouths / alpine peaks at generation time;
|
||||
this importer just loads the pool, exactly as `populate_atlas_city_names`
|
||||
loads the city pool for the settlement placement pipeline.
|
||||
|
||||
Deterministic rebuild: clears atlas_feature_names first, so re-runs are
|
||||
idempotent — there is no UNIQUE(body_id, name, feature_type), so without
|
||||
the clear a re-run would accumulate duplicates. Skips body directories not
|
||||
found in the bodies table (missing FK), and Sol bodies (D-223 permanent
|
||||
exemption — same as `populate_atlas_city_names`, Sol keeps authored
|
||||
geometry-bearing markers.json via sol_import.py, not the names pool).
|
||||
"""
|
||||
valid_body_ids: set[str] = {
|
||||
r[0] for r in conn.execute("SELECT body_id FROM bodies").fetchall()
|
||||
}
|
||||
sol_body_ids: set[str] = {
|
||||
r[0] for r in conn.execute(
|
||||
"SELECT body_id FROM bodies WHERE system_id = 'GJ 0'"
|
||||
).fetchall()
|
||||
}
|
||||
|
||||
rows: list[tuple] = []
|
||||
skipped_bodies: list[str] = []
|
||||
|
||||
pattern = str(WIKI_STAR_SYSTEMS / "*" / "bodies" / "*" / "markers.json")
|
||||
for markers_path in sorted(glob.glob(pattern)):
|
||||
body_id = markers_path.split("/bodies/")[1].split("/")[0]
|
||||
if body_id not in valid_body_ids or body_id in sol_body_ids:
|
||||
if body_id not in valid_body_ids:
|
||||
skipped_bodies.append(body_id)
|
||||
continue
|
||||
|
||||
with open(markers_path) as fh:
|
||||
data = json.load(fh)
|
||||
|
||||
names = data.get("names") or {}
|
||||
for pool_key, feature_type in _FEATURE_NAME_POOL_KEYS.items():
|
||||
for raw_name in names.get(pool_key) or []:
|
||||
name = (raw_name or "").strip()
|
||||
if not name:
|
||||
continue
|
||||
rows.append((body_id, name, feature_type, 0))
|
||||
|
||||
if skipped_bodies:
|
||||
unique = sorted(set(skipped_bodies))
|
||||
print(f" warning: {len(unique)} body dirs not in DB — skipped: {unique[:5]}")
|
||||
|
||||
if not dry_run:
|
||||
conn.execute("DELETE FROM atlas_feature_names")
|
||||
if rows:
|
||||
conn.executemany(
|
||||
"""INSERT INTO atlas_feature_names
|
||||
(body_id, name, feature_type, priority)
|
||||
VALUES (?, ?, ?, ?)""",
|
||||
rows,
|
||||
)
|
||||
|
||||
return len(rows)
|
||||
|
||||
|
||||
# body_type rank for the most_populated_body_in_system LAST-RESORT tiebreak —
|
||||
# lower sorts first. This is NOT "belts can't host settlements" (they can and
|
||||
# DO in this world: GJ845-belt hosts Orkney Ceramics, GJ268-belt hosts Jeju
|
||||
|
||||
@@ -228,6 +228,15 @@ def main() -> None:
|
||||
n_cities = atlas.populate_atlas_city_names(conn, args.dry_run)
|
||||
print(f" {n_cities} city name rows")
|
||||
|
||||
# 12b. atlas_feature_names (rivers/mountains) from wiki markers.json
|
||||
# (D-223, T-1169) — mirrors step 12's pool-load shape exactly, over a
|
||||
# different names-only markers.json key set. Independent of the
|
||||
# settlement pool, so order relative to step 13 doesn't matter; placed
|
||||
# here to stay adjacent to its sibling pool-load step.
|
||||
print(" [12b/13] Populating atlas_feature_names from wiki content...")
|
||||
n_features = atlas.populate_atlas_feature_names(conn, args.dry_run)
|
||||
print(f" {n_features} feature name rows")
|
||||
|
||||
# 13. Standalone-HQ settlements + CityTenant city links (D-242, T-1074) — Phase B.
|
||||
# SUPERSEDES the retired corp-HQ cross-reference (D-207, #909) — that
|
||||
# step inserted one atlas_city_names row per corp HQ with no
|
||||
|
||||
Reference in New Issue
Block a user