feat(tooling): per-system sub-style rotation across all corridors (#833)
Replace single-inflection corridor palettes with lists of sub-styles. Each system picks one deterministically via hash(system_id), so all bodies in the same system share a cultural register but neighbouring systems get different registers. Core corridor splits into 6 sub-styles (English rural, British colonial, US rural, US cosmopolitan, classical/institutional, Australian/NZ). North/south/east/west reach each get 5 sub-styles covering their cultural spectrum. Deep frontier gets 3 (founder-name, surveyor-descriptive, outpost-functional). This multiplies Gemma's effective vocabulary per corridor by the sub-style count, dramatically reducing dedup pressure. A 6-style core corridor means each sub-style serves ~4 systems instead of 24, so "The Ridge" exhausts after ~4 systems, not ~24. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -176,71 +176,95 @@ class Logger:
|
||||
# `star_systems.geographic_sector` directly — that column is the real
|
||||
# source of corridor identity in systems.db (cultural_corridor is a
|
||||
# legacy field that was never populated beyond sol-gateway-axis).
|
||||
CORRIDOR_PALETTES: dict[str, dict[str, str]] = {
|
||||
CORRIDOR_SUBSTYLES: dict[str, list[dict[str, str]]] = {
|
||||
# Each corridor has a list of sub-style inflections. The pipeline
|
||||
# picks one per body via hash(body_id) so neighbouring bodies on the
|
||||
# same planet get different registers, and Gemma's narrow per-register
|
||||
# vocabulary (~15 stems) stays fresh across hundreds of bodies.
|
||||
#
|
||||
# IMPORTANT: the inflection is a DOMINANT bias, not a hard lock.
|
||||
# Earlier iterations used single-culture labels like
|
||||
# "administrative English" or "Korean/Japanese/Taiwanese" which
|
||||
# Gemma 2 2B interpreted as "produce ONLY in this register" — and
|
||||
# every core body came out anglophone, every east_reach body came
|
||||
# out East Asian, etc. The real Earth diaspora in the setting is
|
||||
# cosmopolitan: a British surveyor on an east_reach moon still
|
||||
# names a river after their aunt in Dorset. The inflections now
|
||||
# explicitly name the dominant register AND invite diaspora
|
||||
# variety so Gemma samples from the full cross-cultural few-shot
|
||||
# pool instead of collapsing to the dominant label.
|
||||
"core": {
|
||||
"inflection": "Gateway-era Earth diaspora, mostly English, "
|
||||
"all Earth cultures welcome",
|
||||
"examples": "Meridian, Concord, East Ridge, Landing, Tanaka, "
|
||||
"Rahman, Okafor, Ribeiro",
|
||||
},
|
||||
"north_reach": {
|
||||
"inflection": "British/Australian/Irish dominant, all Earth "
|
||||
"cultures welcome",
|
||||
"examples": "Wolcott, Mildern, Ashbourne, Kiln, Briarfell, "
|
||||
"Tarndale, Ribeiro, Nakamura, Kowalski",
|
||||
},
|
||||
"south_reach": {
|
||||
"inflection": "Portuguese/Swahili/Cape Verdean/Brazilian "
|
||||
"dominant, all Earth cultures welcome",
|
||||
"examples": "Vargas, Inhaca, Monteforte, Serra, Ribeiro, "
|
||||
"Kilimi, Holmberg, Fairview, Tanaka",
|
||||
},
|
||||
"east_reach": {
|
||||
"inflection": "Korean/Japanese/Taiwanese dominant, all Earth "
|
||||
"cultures welcome",
|
||||
"examples": "Hanyang, Takamine, Seoraksan, Tsukuri, Ginoza, "
|
||||
"Baektu, Kellogg, Oliveira, Novak",
|
||||
},
|
||||
"west_reach": {
|
||||
"inflection": "German/Dutch/Nordic dominant, all Earth cultures "
|
||||
"welcome",
|
||||
"examples": "Vanebach, Kloosterdam, Bergfjord, Hellekade, "
|
||||
"Straend, Cooper, Vargas, Nakamura",
|
||||
},
|
||||
"deep_frontier": {
|
||||
"inflection": "frontier founder-name era, any Earth culture",
|
||||
"examples": "Okafor Reach, Stenner Cross, Weller Hold, Pruitt "
|
||||
"Basin, Vickery Hold, Kellogg Run, Nakamura Post, "
|
||||
"Ribeiro Landing",
|
||||
},
|
||||
# Legacy keys retained for backward compatibility with the
|
||||
# cultural_corridor column on the one system that uses it.
|
||||
"sol-gateway-axis": {
|
||||
"inflection": "Gateway-era Earth diaspora, mostly English, "
|
||||
"all Earth cultures welcome",
|
||||
"examples": "Meridian, Concord, Cardinal, Prefecture, Tanaka, "
|
||||
"Rahman, Okafor",
|
||||
},
|
||||
"inner_corridor": {
|
||||
"inflection": "Gateway-era Earth diaspora, mostly English, "
|
||||
"all Earth cultures welcome",
|
||||
"examples": "Meridian, Concord, East Ridge, Landing, Tanaka, "
|
||||
"Rahman, Okafor",
|
||||
},
|
||||
# A British surveyor on an east_reach moon still names a river after
|
||||
# their aunt in Dorset. Each sub-style explicitly names its register
|
||||
# AND invites diaspora variety.
|
||||
"core": [
|
||||
{"inflection": "English countryside, rural, agricultural settlers",
|
||||
"examples": "Thornbury, Bramblewood, Millbrook, Ashford, Weston"},
|
||||
{"inflection": "British colonial settlement era",
|
||||
"examples": "New Bristol, Port Augusta, Kingstown, Admiralty, Georgetown"},
|
||||
{"inflection": "American frontier, practical, geographic",
|
||||
"examples": "Dusty Creek, Twin Oaks, Cedar Flat, Hawk's Hollow, Red Bluff"},
|
||||
{"inflection": "American municipal, administrative, cosmopolitan",
|
||||
"examples": "Prospect Heights, Liberty, Union, Meridian, Commonwealth"},
|
||||
{"inflection": "Classical references, institutional, civic",
|
||||
"examples": "Concordia, Aurelius, Prefecture, Senate Landing, Forum"},
|
||||
{"inflection": "Australian and New Zealand settler",
|
||||
"examples": "Redfern, Glenelg, Wollongong, Kaikoura, Hawke's Bay"},
|
||||
],
|
||||
"north_reach": [
|
||||
{"inflection": "English rural, village and parish names",
|
||||
"examples": "Wolcott, Mildern, Ashbourne, Briarfell, Tarndale"},
|
||||
{"inflection": "Scottish Highland and Lowland place-names",
|
||||
"examples": "Glenmoray, Dunfermline, Kinross, Brae, Dalwhinnie"},
|
||||
{"inflection": "Australian outback, station and property names",
|
||||
"examples": "Redfern, Birdsville, Tennant, Woomera, Coober"},
|
||||
{"inflection": "Irish rural and coastal settlement",
|
||||
"examples": "Ballymore, Kilrush, Dunmore, Tralee, Skellig"},
|
||||
{"inflection": "South African English settler",
|
||||
"examples": "Grahamstown, Oudtshoorn, Stellenbosch, Graaff, Beaufort"},
|
||||
],
|
||||
"south_reach": [
|
||||
{"inflection": "Portuguese colonial era, Iberian",
|
||||
"examples": "Monteforte, Serra, Tavira, Oliveira, Porto Novo"},
|
||||
{"inflection": "Brazilian interior, frontier settlement",
|
||||
"examples": "Ribeirão, Campo Largo, Várzea, Ilhabela, Pinheiro"},
|
||||
{"inflection": "East African Swahili coastal",
|
||||
"examples": "Inhambane, Kilimi, Ngola, Manhica, Quelimane"},
|
||||
{"inflection": "Cape Verdean and West African",
|
||||
"examples": "Cabo, Moçambo, Ribeira, Mindelo, Tarrafal"},
|
||||
{"inflection": "Angolan and Mozambican settlement",
|
||||
"examples": "Huambo, Lobito, Nampula, Lichinga, Benguela"},
|
||||
],
|
||||
"east_reach": [
|
||||
{"inflection": "Korean place-name tradition",
|
||||
"examples": "Hanyang, Seorak, Baektu, Saeyeon, Taegong"},
|
||||
{"inflection": "Japanese rural and coastal settlement",
|
||||
"examples": "Takamine, Ginoza, Tsukuri, Aomori, Fukagawa"},
|
||||
{"inflection": "Taiwanese and Hakka settler",
|
||||
"examples": "Jiufen, Beigang, Hsinchu, Meinong, Tainan"},
|
||||
{"inflection": "Filipino settler community",
|
||||
"examples": "Batangas, Legazpi, Tuguegarao, Zambales, Tarlac"},
|
||||
{"inflection": "Mixed East Asian diaspora, cosmopolitan",
|
||||
"examples": "Naruhan, Morimine, Kōzan, Midori, Kawasaki"},
|
||||
],
|
||||
"west_reach": [
|
||||
{"inflection": "German settlement, orderly and compound names",
|
||||
"examples": "Altdorf, Drachenberg, Feldberg, Krakenberg, Lüneborg"},
|
||||
{"inflection": "Dutch colonial, low-country",
|
||||
"examples": "Kloosterdam, Hoogland, Oudewater, Nieuwpoort, Voorhout"},
|
||||
{"inflection": "Nordic and Scandinavian",
|
||||
"examples": "Sørholm, Torsfell, Bergfjord, Lindeborg, Nordhölm"},
|
||||
{"inflection": "Polish and Czech settler",
|
||||
"examples": "Krakowice, Bystrica, Wieliczka, Tarnów, Ostrava"},
|
||||
{"inflection": "Baltic and Finnish settler",
|
||||
"examples": "Järvenpää, Tallinna, Pärnu, Turku, Rakvere"},
|
||||
],
|
||||
"deep_frontier": [
|
||||
{"inflection": "frontier founder-name era, surname-first, any Earth culture",
|
||||
"examples": "Okafor Reach, Stenner Cross, Weller Hold, Pruitt Basin"},
|
||||
{"inflection": "frontier descriptive, geographic features named by surveyors",
|
||||
"examples": "Red Mesa, Dry Fork, Iron Flat, Long Ridge, Dust Basin"},
|
||||
{"inflection": "frontier outpost, functional and military",
|
||||
"examples": "Forward Post, Relay Station, Survey Camp, Waypoint, Anchor"},
|
||||
],
|
||||
}
|
||||
|
||||
DEFAULT_PALETTE = CORRIDOR_PALETTES["core"]
|
||||
# Legacy aliases
|
||||
CORRIDOR_SUBSTYLES["sol-gateway-axis"] = CORRIDOR_SUBSTYLES["core"]
|
||||
CORRIDOR_SUBSTYLES["inner_corridor"] = CORRIDOR_SUBSTYLES["core"]
|
||||
CORRIDOR_SUBSTYLES["inner_orbit"] = CORRIDOR_SUBSTYLES["core"]
|
||||
CORRIDOR_SUBSTYLES["frontier"] = CORRIDOR_SUBSTYLES["deep_frontier"]
|
||||
|
||||
DEFAULT_SUBSTYLES = CORRIDOR_SUBSTYLES["core"]
|
||||
|
||||
# Processing order for the main run — core first so those bodies win
|
||||
# the dedup race and the outer sectors fall into the palette fallback
|
||||
@@ -255,10 +279,16 @@ SECTOR_PRIORITY: dict[str, int] = {
|
||||
}
|
||||
|
||||
|
||||
def palette_for(corridor: str | None) -> dict[str, str]:
|
||||
if not corridor:
|
||||
return DEFAULT_PALETTE
|
||||
return CORRIDOR_PALETTES.get(corridor, DEFAULT_PALETTE)
|
||||
def palette_for(corridor: str | None, system_id: str = "") -> dict[str, str]:
|
||||
"""Pick a sub-style for this system within its corridor.
|
||||
|
||||
All bodies in the same system get the same sub-style (consistent
|
||||
cultural register per star system). Different systems rotate through
|
||||
the sub-style list via hash(system_id).
|
||||
"""
|
||||
substyles = CORRIDOR_SUBSTYLES.get(corridor or "core", DEFAULT_SUBSTYLES)
|
||||
idx = int(hashlib.sha256(system_id.encode()).hexdigest()[:8], 16) % len(substyles)
|
||||
return substyles[idx]
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
@@ -1315,6 +1345,7 @@ def name_feature(
|
||||
stem_counts: dict[str, int],
|
||||
stem_cap: int,
|
||||
system_hook: str | None,
|
||||
system_id: str,
|
||||
world_seed: int,
|
||||
body_id: str,
|
||||
local_id: str,
|
||||
@@ -1340,7 +1371,7 @@ def name_feature(
|
||||
if feature_type not in _PROMPT_CONFIG:
|
||||
return fallback_name(corridor, feature_type, _seed_for(world_seed, body_id, local_id, 0))
|
||||
|
||||
palette = palette_for(corridor)
|
||||
palette = palette_for(corridor, system_id)
|
||||
planet_class = ctx.get("planet_class") or "habitable"
|
||||
|
||||
dedup_key = (hop, corridor, feature_type)
|
||||
@@ -1633,7 +1664,7 @@ def process_body(
|
||||
feature_type = _feature_type_for_city(city)
|
||||
name = name_feature(
|
||||
voice, feature_type, ctx, blocklist, corpus, body_used,
|
||||
stem_counts, stem_cap, system_hook,
|
||||
stem_counts, stem_cap, system_hook, system_id,
|
||||
world_seed, body_id, city.get("id") or "city_?",
|
||||
hop, log, verbose,
|
||||
)
|
||||
@@ -1651,7 +1682,7 @@ def process_body(
|
||||
continue
|
||||
name = name_feature(
|
||||
voice, "river", ctx, blocklist, corpus, body_used,
|
||||
stem_counts, stem_cap, system_hook,
|
||||
stem_counts, stem_cap, system_hook, system_id,
|
||||
world_seed, body_id, river.get("id") or "river_?",
|
||||
hop, log, verbose,
|
||||
)
|
||||
@@ -1672,7 +1703,7 @@ def process_body(
|
||||
feature_type = _feature_type_for_ocean(water)
|
||||
name = name_feature(
|
||||
voice, feature_type, ctx, blocklist, corpus, body_used,
|
||||
stem_counts, stem_cap, system_hook,
|
||||
stem_counts, stem_cap, system_hook, system_id,
|
||||
world_seed, body_id, water.get("id") or "water_?",
|
||||
hop, log, verbose,
|
||||
)
|
||||
@@ -1692,7 +1723,7 @@ def process_body(
|
||||
continue
|
||||
name = name_feature(
|
||||
voice, "mountain_range", ctx, blocklist, corpus, body_used,
|
||||
stem_counts, stem_cap, system_hook,
|
||||
stem_counts, stem_cap, system_hook, system_id,
|
||||
world_seed, body_id, rng_feat.get("id") or "range_?",
|
||||
hop, log, verbose,
|
||||
)
|
||||
@@ -1713,7 +1744,7 @@ def process_body(
|
||||
feature_type = _feature_type_for_poi(poi)
|
||||
name = name_feature(
|
||||
voice, feature_type, ctx, blocklist, corpus, body_used,
|
||||
stem_counts, stem_cap, system_hook,
|
||||
stem_counts, stem_cap, system_hook, system_id,
|
||||
world_seed, body_id, poi.get("id") or "poi_?",
|
||||
hop, log, verbose,
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user