feat(tooling): skip dedup-exhausted features instead of palette fallback (#833)
When all 3 LLM attempts are rejected (dedup, blocklist, etc.), name_feature now returns None instead of a deterministic palette fallback. process_body leaves the name as null in markers.json. The preserved path (_is_blank) treats null as unnamed, so a fill round (re-running the script) picks up only the skipped features with a fresh corpus — zero dedup pressure from the first pass. The fill round can use a different seed, slower prompt, or a different backend entirely (e.g. Haiku). Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -1450,21 +1450,12 @@ def name_feature(
|
||||
_commit_name(cleaned)
|
||||
return cleaned
|
||||
|
||||
# All attempts exhausted — deterministic palette fallback, then dedup.
|
||||
# Fallback names draw from the palette stems and do NOT count against
|
||||
# the stem cap (the palette is intentionally narrow and would trigger
|
||||
# infinite rejection loops otherwise).
|
||||
salt = _seed_for(world_seed, body_id, local_id, max_attempts) & 0xFFFFFF
|
||||
fallback = fallback_name(corridor, feature_type, salt)
|
||||
bump = 0
|
||||
while _is_duplicate(fallback) and bump < 100:
|
||||
bump += 1
|
||||
fallback = fallback_name(corridor, feature_type, salt + bump)
|
||||
used.add(fallback)
|
||||
body_used.add(fallback)
|
||||
# All attempts exhausted. Leave the name as None so the preserved
|
||||
# path skips it on a fill round — a second pass with a fresh corpus
|
||||
# will pick it up and try again with different dedup pressure.
|
||||
reasons = " ".join(f"{k}={v}" for k, v in rejections.items() if v > 0)
|
||||
log(f" fallback: {body_id}/{local_id} → '{fallback}' [{reasons}]")
|
||||
return fallback
|
||||
log(f" skipped: {body_id}/{local_id} [{reasons}]")
|
||||
return None
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
@@ -1646,10 +1637,11 @@ def process_body(
|
||||
world_seed, body_id, city.get("id") or "city_?",
|
||||
hop, log, verbose,
|
||||
)
|
||||
city["name"] = name
|
||||
counts["cities"] += 1
|
||||
generated["cities"].append(name)
|
||||
changed = True
|
||||
if name is not None:
|
||||
city["name"] = name
|
||||
counts["cities"] += 1
|
||||
generated["cities"].append(name)
|
||||
changed = True
|
||||
|
||||
# Rivers
|
||||
for river in markers.get("rivers") or []:
|
||||
@@ -1663,10 +1655,11 @@ def process_body(
|
||||
world_seed, body_id, river.get("id") or "river_?",
|
||||
hop, log, verbose,
|
||||
)
|
||||
river["name"] = name
|
||||
counts["rivers"] += 1
|
||||
generated["rivers"].append(name)
|
||||
changed = True
|
||||
if name is not None:
|
||||
river["name"] = name
|
||||
counts["rivers"] += 1
|
||||
generated["rivers"].append(name)
|
||||
changed = True
|
||||
|
||||
# Oceans / seas / lakes
|
||||
for water in markers.get("oceans") or []:
|
||||
@@ -1683,10 +1676,11 @@ def process_body(
|
||||
world_seed, body_id, water.get("id") or "water_?",
|
||||
hop, log, verbose,
|
||||
)
|
||||
water["name"] = name
|
||||
counts["oceans"] += 1
|
||||
generated["oceans"].append(name)
|
||||
changed = True
|
||||
if name is not None:
|
||||
water["name"] = name
|
||||
counts["oceans"] += 1
|
||||
generated["oceans"].append(name)
|
||||
changed = True
|
||||
|
||||
# Mountain ranges
|
||||
for rng_feat in markers.get("mountain_ranges") or []:
|
||||
@@ -1702,10 +1696,11 @@ def process_body(
|
||||
world_seed, body_id, rng_feat.get("id") or "range_?",
|
||||
hop, log, verbose,
|
||||
)
|
||||
rng_feat["name"] = name
|
||||
counts["mountain_ranges"] += 1
|
||||
generated["mountain_ranges"].append(name)
|
||||
changed = True
|
||||
if name is not None:
|
||||
rng_feat["name"] = name
|
||||
counts["mountain_ranges"] += 1
|
||||
generated["mountain_ranges"].append(name)
|
||||
changed = True
|
||||
|
||||
# POIs
|
||||
for poi in markers.get("pois") or []:
|
||||
@@ -1722,10 +1717,11 @@ def process_body(
|
||||
world_seed, body_id, poi.get("id") or "poi_?",
|
||||
hop, log, verbose,
|
||||
)
|
||||
poi["name"] = name
|
||||
counts["pois"] += 1
|
||||
generated["pois"].append(name)
|
||||
changed = True
|
||||
if name is not None:
|
||||
poi["name"] = name
|
||||
counts["pois"] += 1
|
||||
generated["pois"].append(name)
|
||||
changed = True
|
||||
|
||||
if changed:
|
||||
markers_path.write_text(json.dumps(markers, indent=2) + "\n")
|
||||
|
||||
Reference in New Issue
Block a user