docs(architecture): atlas naming pipeline reference (#833)
Full architecture doc covering the Gemma 4 batch naming pipeline: pipeline stages, cultural registers, body ordering, known limitations, QA process, and extension guide. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,488 @@
|
||||
# Atlas Naming Pipeline
|
||||
|
||||
Reference document for the feature-naming pipeline that populates
|
||||
`markers.json` files and the `atlas_*` tables in `systems.db`.
|
||||
|
||||
Related: ticket #833, D-191 §4.
|
||||
|
||||
---
|
||||
|
||||
## 1. Overview
|
||||
|
||||
The atlas naming pipeline generates human-readable names for every
|
||||
unnamed geographic feature across ~2,394 planetary bodies in the
|
||||
Settled Reach. Features covered: cities (capital and secondary),
|
||||
rivers, oceans/seas/lakes, mountain ranges, and points of interest
|
||||
(transit, institutional, cultural).
|
||||
|
||||
Inputs:
|
||||
- `wiki/star-systems/*/bodies/*/markers.json` — one file per body,
|
||||
containing arrays of feature records with optional `name` fields
|
||||
- `server/data/systems.db` — `star_systems`, `bodies`, `atlas_*` tables
|
||||
- `wiki/star-systems/*/index.md` and `gttr.md` — cultural context for
|
||||
register selection
|
||||
- `tooling/planet-gen/earth_blocklist.txt` — exact-match rejection list
|
||||
for well-known Earth place names
|
||||
|
||||
Outputs:
|
||||
- `markers.json` files updated in-place with the new `name` fields
|
||||
- `atlas_cities`, `atlas_rivers`, `atlas_mountain_ranges`, `atlas_oceans`,
|
||||
`atlas_pois` rows in `systems.db` synced immediately after each body
|
||||
|
||||
Hand-authored names in `markers.json` are never overwritten. Re-running the
|
||||
pipeline is safe: any body whose features all have non-empty `name` fields is
|
||||
skipped entirely.
|
||||
|
||||
Wall time on an RX 9070 (ROCm) is roughly 4-6 hours for a full run.
|
||||
|
||||
---
|
||||
|
||||
## 2. Architecture
|
||||
|
||||
### The inference binary: sr-voice-tooling
|
||||
|
||||
The pipeline does NOT use the game's Gemma 2 runtime (`sr-voice`). It uses a
|
||||
separate binary, `sr-voice-tooling`, built against Gemma 4 (GGUF) and compiled
|
||||
for the ROCm / HIP stack. The binary lives outside all git worktrees at:
|
||||
|
||||
```
|
||||
~/Projects/settled-reach/binaries/sr-voice-tooling
|
||||
~/Projects/settled-reach/models/gemma-4.gguf
|
||||
```
|
||||
|
||||
The split exists because the game's runtime (Gemma 2, 2B parameters, 1024-token
|
||||
context) is optimised for low-latency in-game NPC dialogue, while the tooling
|
||||
binary trades latency for richer outputs and a larger context window.
|
||||
|
||||
### stdio JSONL protocol
|
||||
|
||||
`VoiceSubprocess` in `gemma_naming.py` manages the binary as a long-lived
|
||||
subprocess. Communication is newline-delimited JSON on stdin/stdout:
|
||||
|
||||
Request (one JSON object per line):
|
||||
```json
|
||||
{"prompt": "...", "seed": 12345678}
|
||||
```
|
||||
|
||||
Response:
|
||||
```json
|
||||
{"text": "..."}
|
||||
```
|
||||
|
||||
or on error:
|
||||
```json
|
||||
{"error": "..."}
|
||||
```
|
||||
|
||||
The subprocess is restarted every `--refresh` requests (default: 1000) to
|
||||
prevent KV-cache context bleed accumulating across bodies. Seeds are derived
|
||||
deterministically from `(body_id, feature_type, world_seed)` so the same
|
||||
prompt always produces the same output — re-running for a partially-completed
|
||||
body reproduces the same names, which helps diagnose quality issues without
|
||||
introducing fresh variation.
|
||||
|
||||
### Running inside distrobox
|
||||
|
||||
The ROCm HIP kernels embedded in `sr-voice-tooling` are compiled for gfx1201
|
||||
(AMD Navi 48 / RX 9070). Because `libhipblas.so.2` and related ROCm libraries
|
||||
are only installed in the `reach-build` distrobox container, `run-atlas-naming.sh`
|
||||
and the test scripts invoke the binary via `distrobox enter reach-build --`.
|
||||
The shell wrapper verifies the gfx1201 target is present before starting.
|
||||
|
||||
---
|
||||
|
||||
## 3. Pipeline Stages
|
||||
|
||||
For each body, `process_body()` executes the following stages in order.
|
||||
|
||||
### Stage 1: Register selection (per system, wiki-grounded)
|
||||
|
||||
At the start of each new system, `select_register()` asks Gemma to choose
|
||||
the best cultural register from the corridor's `CORRIDOR_SUBSTYLES` list,
|
||||
based on the system's `wiki/star-systems/{slug}/index.md` and `gttr.md`.
|
||||
|
||||
`load_wiki_context()` reads both files. `_extract_cultural_lines()` scans
|
||||
`index.md` for lines containing cultural keywords (heritage, founding, settler,
|
||||
diaspora, specific language names, etc.) and returns up to 8 of them. The first
|
||||
substantive paragraph from `gttr.md` (skipping the title line) is used as the
|
||||
context hook.
|
||||
|
||||
The prompt is few-shot: two worked examples from different corridors, then the
|
||||
target system's context followed by the numbered option list. Gemma replies
|
||||
with a single digit. The pipeline retries up to `max_attempts=3` times with
|
||||
rotated seeds. On failure it falls back to `palette_for()`, a hash-based
|
||||
deterministic selection: `hash(system_id) % len(substyles)`.
|
||||
|
||||
The selected register applies to all bodies in that system. All bodies in the
|
||||
same system therefore share a consistent cultural voice.
|
||||
|
||||
### Stage 2: Mood injection (per body)
|
||||
|
||||
`mood_for_body(body_id, world_seed)` picks one entry from `MOOD_POOL` via
|
||||
`SHA-256(f"mood|{body_id}|{world_seed}")`. The 13-word pool is:
|
||||
|
||||
```
|
||||
ambition, family, wealth, community, industry, pride, fleeting,
|
||||
hope, fear, isolation, devotion, defiance, loss
|
||||
```
|
||||
|
||||
The chosen mood is injected into each batch prompt as a clause:
|
||||
`"The settlers here had a sense of {mood}."` This is the primary mechanism for
|
||||
vocabulary divergence across bodies in the same system — same cultural register,
|
||||
different emotional colouring. The mood is deterministic per body so re-runs
|
||||
produce identical results.
|
||||
|
||||
### Stage 3: Batch naming (2x oversampled)
|
||||
|
||||
`name_features_batch()` (in `naming_core.py`) groups blank features by type and
|
||||
requests `count * 2` names in a single prompt call. The oversample factor means
|
||||
the ranking step (Stage 4) has candidates to work with even if the model repeats
|
||||
some names.
|
||||
|
||||
`build_batch_prompt()` assembles the prompt with:
|
||||
- A preamble emphasising mundane, practical settler names over epic or classical
|
||||
ones
|
||||
- Optional system name, body name, and `system_hook` (the gttr.md
|
||||
characterisation)
|
||||
- The mood clause (if a mood was injected)
|
||||
- The "already used" list (trimmed to fit within `ctx_size` tokens)
|
||||
- Two fixed few-shot examples showing comma-separated batch format
|
||||
(Scottish Highland and Dutch colonial)
|
||||
- The tail: `"Style: {inflection}. {count*2} names:"`
|
||||
|
||||
The model pattern-completes a comma-separated list. `parse_batch_response()`
|
||||
takes the first line only (the model often continues with explanation), splits
|
||||
on commas, strips quotes, filters via `is_valid_name()`, and deduplicates
|
||||
preserving order.
|
||||
|
||||
For single-name prompts (the legacy `_build_prompt()` / `name_feature()` path),
|
||||
the prompt uses a rotating pool of few-shot style+answer pairs. The pool is
|
||||
picked deterministically by `hash(body_id | local_id | attempt)` so retries
|
||||
rotate to a different example set rather than re-querying with the same prompt
|
||||
and a different seed.
|
||||
|
||||
### Stage 4: Levenshtein distinctiveness ranking
|
||||
|
||||
`select_distinct(candidates, count, taken)` in `naming_core.py` greedily
|
||||
selects the N most distinct names from the oversampled candidate list.
|
||||
|
||||
The distinctiveness metric is `word_avg_distance()`: for each word in the
|
||||
shorter name, find the closest word (by Levenshtein edit distance) in the
|
||||
longer name, then average across words. This means shared structural words
|
||||
(Serra, The, Glen, Ridge) lower a candidate's preference score but do not
|
||||
block it outright — the unique words in the name pull the average up.
|
||||
|
||||
Selection is greedy: pick the candidate with the highest minimum distance to
|
||||
all already-selected names plus all `taken` names, add it to the selected set,
|
||||
repeat. No hard threshold — all candidates except exact string matches to
|
||||
`taken` are eligible.
|
||||
|
||||
### Stage 5: Cross-body dedup
|
||||
|
||||
The `corpus` dict in `main()` maps `(system_id, feature_type)` to the set of
|
||||
all names already assigned within that system. Before each batch call, `taken`
|
||||
is built from `corpus[(system_id, ft)]` plus `body_used` (all names already
|
||||
assigned to the current body across all feature types). This enforces two
|
||||
constraints:
|
||||
|
||||
- No body has the same name for two different features (e.g. a river and a
|
||||
mountain range).
|
||||
- No two bodies in the same system share the same name for the same feature
|
||||
type (e.g. two rivers in GJ 144 named "Cooper's Creek").
|
||||
|
||||
Cross-corridor collisions (the same name appearing in two different systems
|
||||
from different corridors) are allowed and expected.
|
||||
|
||||
`taken` is pruned when it exceeds the context budget: the most recently added
|
||||
entries are dropped first. This means a very large system (many bodies, many
|
||||
features) may eventually lose dedup coverage for its earliest names at the
|
||||
tail of the run.
|
||||
|
||||
### Stage 6: Adjacent-register refill
|
||||
|
||||
If the batch call + ranking still yields fewer than `count` names (after
|
||||
dedup against `taken`), `name_features_batch()` fires a refill call using the
|
||||
next sub-style in the corridor's `CORRIDOR_SUBSTYLES` list, wrapping around.
|
||||
The refill asks for `shortfall * 3` candidates. Refill results go through the
|
||||
same `select_distinct()` ranking against `taken + selected`. This ensures
|
||||
the feature list is always fully populated even when the primary register's
|
||||
vocabulary is exhausted.
|
||||
|
||||
---
|
||||
|
||||
## 4. Cultural Registers
|
||||
|
||||
### CORRIDOR_SUBSTYLES
|
||||
|
||||
`CORRIDOR_SUBSTYLES` in `gemma_naming.py` is a `dict[str, list[dict[str, str]]]`
|
||||
mapping corridor name to an ordered list of sub-style dicts. Each sub-style has
|
||||
two keys: `inflection` (the style label injected into the prompt) and `examples`
|
||||
(a comma-separated string of illustrative names, shown during `select_register()`
|
||||
option display but NOT injected into batch prompts — only the inflection label
|
||||
reaches the model during naming).
|
||||
|
||||
Active corridors and their sub-styles:
|
||||
|
||||
**core** (6 sub-styles)
|
||||
- English countryside, rural, agricultural settlers
|
||||
- British colonial settlement era
|
||||
- American frontier, practical, geographic
|
||||
- American municipal, administrative, cosmopolitan
|
||||
- Classical references, institutional, civic
|
||||
- Australian and New Zealand settler
|
||||
|
||||
**north_reach** (5 sub-styles)
|
||||
- English rural, village and parish names
|
||||
- Scottish Highland and Lowland place-names
|
||||
- Australian outback, station and property names
|
||||
- Irish rural and coastal settlement
|
||||
- South African English settler
|
||||
|
||||
**south_reach** (5 sub-styles)
|
||||
- Portuguese colonial era, Iberian
|
||||
- Brazilian interior, frontier settlement
|
||||
- East African Swahili coastal
|
||||
- Cape Verdean and West African
|
||||
- Angolan and Mozambican settlement
|
||||
|
||||
**east_reach** (5 sub-styles)
|
||||
- Korean place-name tradition
|
||||
- Japanese rural and coastal settlement
|
||||
- Taiwanese and Hakka settler
|
||||
- Filipino settler community
|
||||
- Mixed East Asian diaspora, cosmopolitan
|
||||
|
||||
**west_reach** (5 sub-styles)
|
||||
- German settlement, orderly and compound names
|
||||
- Dutch colonial, low-country
|
||||
- Nordic and Scandinavian
|
||||
- Polish and Czech settler
|
||||
- Baltic and Finnish settler
|
||||
|
||||
**deep_frontier** (3 sub-styles)
|
||||
- frontier founder-name era, surname-first, any Earth culture
|
||||
- frontier descriptive, geographic features named by surveyors
|
||||
- frontier outpost, functional and military
|
||||
|
||||
Legacy aliases (`sol-gateway-axis`, `inner_corridor`, `inner_orbit`) map to
|
||||
`core`. `frontier` maps to `deep_frontier`. The pipeline reads the corridor
|
||||
from `COALESCE(cultural_corridor, geographic_sector, 'core')` in
|
||||
`star_systems` — `geographic_sector` is the live field; `cultural_corridor`
|
||||
was a legacy column never populated beyond the sol-gateway-axis.
|
||||
|
||||
### Sub-style rotation
|
||||
|
||||
All bodies within a system use the same register (chosen by `select_register()`
|
||||
or `palette_for()`). Across systems within a corridor the hash-based fallback
|
||||
rotates through the sub-style list: `hash(system_id) % len(substyles)`. This
|
||||
ensures neighbouring systems don't all sound identical even when `select_register()`
|
||||
fails.
|
||||
|
||||
### Inflection as dominant bias, not hard lock
|
||||
|
||||
The `CORRIDOR_SUBSTYLES` comment is explicit: the inflection is a dominant bias,
|
||||
not a hard cultural lock. A British surveyor on an east_reach moon still names
|
||||
a river after their aunt in Dorset. The few-shot examples in the batch prompt
|
||||
show cross-cultural names to reinforce this — the examples are Scottish Highland
|
||||
and Dutch colonial regardless of the target corridor.
|
||||
|
||||
### SECTOR_PRIORITY
|
||||
|
||||
`SECTOR_PRIORITY` sets the processing order: `core=0`, `north_reach=1`,
|
||||
`south_reach=2`, `east_reach=3`, `west_reach=4`, `deep_frontier=5`. Core
|
||||
bodies are named first so they win the dedup race; frontier bodies fall into
|
||||
the adjacent-register refill path when names collide.
|
||||
|
||||
---
|
||||
|
||||
## 5. Body Processing Order
|
||||
|
||||
`discover_bodies()` discovers all `markers.json` files via
|
||||
`WIKI_SYSTEMS.glob("*/bodies/*/markers.json")` and sorts them by a 5-tuple
|
||||
sort key loaded from `load_body_hop_order()`:
|
||||
|
||||
```
|
||||
(hop_distance, system_id, is_inhabited_desc, population_rank, body_id)
|
||||
```
|
||||
|
||||
- **Hop distance** from Gateway (GJ 71), ascending. Core systems process first.
|
||||
- **System grouping**: all bodies in a system are processed consecutively
|
||||
(same `system_id` in the sort key).
|
||||
- **Inhabited first**: within a system, inhabited bodies sort before
|
||||
uninhabited ones.
|
||||
- **Population rank**: within inhabited bodies, higher-population bodies sort
|
||||
earlier (so the main inhabited world of a system wins the dedup race against
|
||||
minor colonies).
|
||||
- Bodies whose `body_id` is absent from `hop_order` (markers.json orphans
|
||||
not in the DB) sort to the end with a sentinel hop of 99.
|
||||
|
||||
---
|
||||
|
||||
## 6. Key Files and Their Roles
|
||||
|
||||
| File | Role |
|
||||
|------|------|
|
||||
| `tooling/planet-gen/gemma_naming.py` | Main pipeline: `VoiceSubprocess`, `CORRIDOR_SUBSTYLES`, `palette_for()`, `select_register()`, `_build_prompt()` (single-name path), `_PROMPT_CONFIG`, `process_body()`, `main()`. |
|
||||
| `tooling/planet-gen/naming_core.py` | Shared algorithms: `levenshtein()`, `word_avg_distance()`, `select_distinct()`, `build_batch_prompt()`, `parse_batch_response()`, `name_features_batch()`, `mood_for_body()`, `MOOD_POOL`, `is_valid_name()`. |
|
||||
| `tooling/planet-gen/run-atlas-naming.sh` | Shell wrapper for overnight runs. Validates the binary (`gfx1201` kernel check), model, and distrobox container before exec-ing into `gemma_naming.py`. Hardcodes paths — no arguments accepted. |
|
||||
| `tooling/planet-gen/qa_naming.py` | QA report: reads `systems.db` and `markers.json` files, runs the full check suite, prints a summary with issue rates. |
|
||||
| `tooling/planet-gen/test_batch_naming.py` | Integration test harness. Runs 4 simulated systems (20 bodies) against real Gemma 4 via `sr-voice-tooling`, accumulates the taken list across bodies, prints candidates/selected/refill for visual inspection. |
|
||||
| `tooling/planet-gen/test_register_selection.py` | Tests `select_register()` against 8 real systems spanning all corridors, prints what Gemma picks vs. the hash-based fallback. |
|
||||
| `tooling/planet-gen/earth_blocklist.txt` | Lowercase exact-match list of Earth major place names. `is_blocked()` also strips a leading "The " before comparing. |
|
||||
| `~/Projects/settled-reach/binaries/sr-voice-tooling` | The Gemma 4 inference binary. Lives outside the repo. Built with `CMAKE_HIP_ARCHITECTURES=gfx1201` inside the `reach-build` distrobox. |
|
||||
| `~/Projects/settled-reach/models/gemma-4.gguf` | The model weights. Shared across worktrees. |
|
||||
| `server/data/systems.db` | SQLite database. `star_systems.geographic_sector` is the corridor source of truth. `atlas_*` tables are synced per body immediately after `markers.json` is written. |
|
||||
|
||||
`generate_atlas.py` provides `ensure_atlas_schema()` and `sync_markers_to_db()`,
|
||||
both imported by `gemma_naming.py`. This makes `generate_atlas.py` the single
|
||||
authoritative path for `atlas_*` row writes.
|
||||
|
||||
---
|
||||
|
||||
## 7. Known Limitations
|
||||
|
||||
### Classical register sameyness
|
||||
|
||||
The "Classical references, institutional, civic" sub-style in the `core`
|
||||
corridor (Concordia, Aurelius, Prefecture, Forum) has a narrow vocabulary.
|
||||
Gemma 4 has roughly 15 usable stems per register. On a system with many
|
||||
bodies, classical register bodies at the tail of the run exhaust the fresh
|
||||
stem pool and the adjacent-register refill kicks in. The refill names are
|
||||
structurally correct but may not feel classical. This is by design: the
|
||||
frontier fallback is correct behaviour, not a failure mode.
|
||||
|
||||
### Few-shot example bleed
|
||||
|
||||
The two fixed batch few-shot examples (Scottish Highland, Dutch colonial) are
|
||||
cross-corridor by design, but on short prompts (low `ctx_size`) they can
|
||||
dominate the model's completion distribution. The result is names that have
|
||||
a vaguely British or Dutch character even for east_reach or south_reach
|
||||
bodies. Increasing `ctx_size` reduces this; at 1024 tokens it is detectable
|
||||
but not severe.
|
||||
|
||||
### Narrow register exhaustion on large systems
|
||||
|
||||
The `taken` list is trimmed from the tail when it exceeds the token budget.
|
||||
For systems with 30+ bodies (e.g. very large multi-moon systems), early
|
||||
names may drop off the `taken` list before all bodies are processed, allowing
|
||||
duplicates to slip through. `qa_naming.py`'s `check_exact_dupes_within_system()`
|
||||
catches these; manual re-runs with `--body` are the fix.
|
||||
|
||||
### Stem repetition ("Ridge Ridge Ridge")
|
||||
|
||||
`check_stem_repetition()` detects this: 4 or more features on the same body
|
||||
sharing the same first word. It is caused by the model having a strong
|
||||
associative path from the inflection to one compound-word opener (e.g.
|
||||
"Ridge" for frontier descriptive). The Levenshtein ranking partially mitigates
|
||||
it — two names with identical first words have a low `word_avg_distance` and
|
||||
the second one loses the greedy selection — but it does not eliminate it when
|
||||
the entire candidate pool shares the stem.
|
||||
|
||||
### Single-name vs. batch path
|
||||
|
||||
`gemma_naming.py` contains two prompt-building paths: the legacy
|
||||
`_build_prompt()` / `name_feature()` single-name path and the newer
|
||||
`build_batch_prompt()` / `name_features_batch()` batch path. The batch path
|
||||
is what `process_body()` calls via `_batch_fill()`. The single-name path
|
||||
and its `_PROMPT_CONFIG` pools are still present and used by `_build_prompt()`,
|
||||
which is called by `name_feature()` — that function may be invoked on retry
|
||||
paths or by older call sites. The two paths use different prompt structures
|
||||
and produce different output distributions.
|
||||
|
||||
---
|
||||
|
||||
## 8. QA Process
|
||||
|
||||
### Running
|
||||
|
||||
```bash
|
||||
python3 tooling/planet-gen/qa_naming.py
|
||||
python3 tooling/planet-gen/qa_naming.py --verbose
|
||||
```
|
||||
|
||||
Requires `server/data/systems.db` to be present with populated `atlas_*` tables.
|
||||
|
||||
### What the checks look for
|
||||
|
||||
| Check | Function | Trigger |
|
||||
|-------|----------|---------|
|
||||
| Prompt fragment leaks | `check_prompt_fragments()` | Name contains substrings like `"style:"`, `"generate"`, `"already used"`, `"must be"`, etc. |
|
||||
| Exact dupes within system | `check_exact_dupes_within_system()` | Same name, same feature type, two different bodies in the same system. |
|
||||
| Exact dupes within body | `check_exact_dupes_within_body()` | Same name, different feature types, same body (e.g. a river and a mountain range both called "Thornbury"). |
|
||||
| Stem repetition | `check_stem_repetition()` | 4 or more features of the same type on a body share the same first word. |
|
||||
| Body name echo | `check_body_name_echo()` | A word from the body's proper name appears as a word in a feature name. |
|
||||
| Register bleed | `check_register_bleed()` | Keyword heuristics flag obviously wrong cultural associations (e.g. "fjord" in south_reach, "sakura" in west_reach). |
|
||||
| Feature type mismatch | `check_feature_type_mismatch()` | Street-vocabulary words on a mountain; building-vocabulary words on a river or ocean. |
|
||||
| Short names | `check_short_long_names()` | Name is 3 characters or fewer. |
|
||||
| Long names | `check_short_long_names()` | Name is more than 40 characters. |
|
||||
| Numbers in names | `check_numbers_in_names()` | Name contains a digit. |
|
||||
| Coverage gaps | `coverage_gaps()` | Bodies with one or more unnamed features remaining in `atlas_*` tables. Inhabited bodies listed separately. |
|
||||
|
||||
The summary block at the end prints a total issue rate (`issues / total_named *
|
||||
100`). A clean run at the end of a full batch should sit below 1%. Prompt
|
||||
fragment leaks above zero indicate a context-budget or parsing failure and
|
||||
should be investigated before ship.
|
||||
|
||||
---
|
||||
|
||||
## 9. How to Extend
|
||||
|
||||
### Adding a new cultural register
|
||||
|
||||
1. Add a new dict entry to the relevant list in `CORRIDOR_SUBSTYLES`:
|
||||
```python
|
||||
{"inflection": "...", "examples": "..."}
|
||||
```
|
||||
2. The pipeline picks it up automatically. `palette_for()` uses
|
||||
`len(substyles)` for the modulo, so the new sub-style becomes available
|
||||
on the next hash rotation. `select_register()` presents it as a numbered
|
||||
option to Gemma.
|
||||
3. Run `test_register_selection.py` to verify Gemma picks the new register
|
||||
for a system whose wiki content matches it.
|
||||
|
||||
### Tuning temperature and sampling
|
||||
|
||||
Temperature is a parameter of the `sr-voice-tooling` binary, not of the
|
||||
Python scripts. Pass flags to the binary via the `--sr-voice` argument or
|
||||
edit `run-atlas-naming.sh`. The scripts have no Python-side temperature knob.
|
||||
|
||||
### Adding new moods
|
||||
|
||||
Extend `MOOD_POOL` in `naming_core.py`. The deterministic hash spreads the
|
||||
new mood across bodies automatically on the next full run. Existing bodies
|
||||
whose `markers.json` already has names will be skipped, so only un-named
|
||||
bodies pick up the new mood entries.
|
||||
|
||||
### Running for a single system or body
|
||||
|
||||
Single body:
|
||||
```bash
|
||||
python3 tooling/planet-gen/gemma_naming.py --body GJ380c
|
||||
```
|
||||
|
||||
Single body with verbose output showing per-feature retry detail:
|
||||
```bash
|
||||
python3 tooling/planet-gen/gemma_naming.py --body GJ380c --verbose
|
||||
```
|
||||
|
||||
Smoke test (first 5 bodies in hop order):
|
||||
```bash
|
||||
python3 tooling/planet-gen/gemma_naming.py --limit 5 --verbose
|
||||
```
|
||||
|
||||
Without a real model (mock stdio for pipeline testing):
|
||||
```bash
|
||||
python3 tooling/planet-gen/gemma_naming.py --mock --limit 5
|
||||
```
|
||||
|
||||
Dump prompts to JSONL without calling the model (for A/B comparison against
|
||||
a different backend):
|
||||
```bash
|
||||
python3 tooling/planet-gen/gemma_naming.py \
|
||||
--dump-prompts /tmp/prompts.jsonl \
|
||||
--limit 10
|
||||
```
|
||||
|
||||
All invocations use `--refresh 1000` by default (subprocess restart every
|
||||
1000 requests). Lower it to `--refresh 100` if you observe quality degradation
|
||||
across a long single-system run.
|
||||
Reference in New Issue
Block a user