# Conflicts: # CHANGELOG.md # content/_meta/README.md # content/_meta/npc-authoring-style-guide.md # wiki/_templates/cultural-group.md # wiki/_templates/institution.md # wiki/_templates/star-system.md # wiki/characters/devra.md # wiki/characters/drin.md # wiki/characters/harek.md # wiki/characters/lera-sessik.md # wiki/characters/maret-korr.md # wiki/characters/naia-tamm.md # wiki/characters/nils-davan.md # wiki/characters/pell.md # wiki/characters/renn.md # wiki/characters/resha.md # wiki/characters/sabel.md # wiki/characters/sera-venn.md # wiki/characters/torek-lintar.md # wiki/characters/voss.md # wiki/star-systems/krenn/index.md
21 KiB
Culture Authoring Guide — The Settled Reach
Ticket: #653 Author: Mellanie Status: canonical Last updated: 2026-03-13
Purpose
Every NPC in The Settled Reach is voiced through their culture. The voice pipeline (D-138) uses a culture profile as its primary LLM prompt — the culture injector is the single biggest determinant of how an NPC sounds. Culture files are authored once and used thousands of times across every generated NPC in that system.
This guide explains how to author a new culture RON file from scratch. The target: one experienced author can produce a complete, Spike-validated culture profile in approximately one working day.
Three reference cultures exist in content/global/:
culture-van-maanens-star.ron— Mid-Reach, freight, working-class pragmatic (the Spike 1/2 reference)culture-vael.ron— Core-adjacent, administrative/research, institutional-formalculture-osse.ron— Deep Frontier, extraction industry, terse-utilitarian
Step 1: Establish the cultural parameters (30 minutes)
Before writing a single word of the RON file, answer these five questions from docs/design/cultural-generation-guide.md:
-
Reach Position: Core / Mid-Reach / Frontier / Deep Frontier → Determines authority attitude, institutional density, Commission presence
-
Economic Base: Freight / Extraction / Manufacturing / Service / Research / Mixed → Determines daily vocabulary, rhythms, what NPCs care about
-
Settlement Age: New (< 20 yr) / Established (20-80 yr) / Mature (80-200 yr) / Old (200+ yr) → Determines rootedness, architectural character, whether "here" is home
-
Heritage Root: Frost / Tide / Iron / Dust / Stone / Vine / Salt → Determines spatial grammar and social privacy model (D-104/D-105)
-
Institutional Ecosystem: Which institutions are visibly present? Which are resented?
Write these down before you open a RON file. They are the source of truth for everything that follows.
Step 2: Define the voice register (10 minutes)
Derive the voice register from your five parameters. Ask:
- How does authority feel here? (Collegial / Distant / Resented / Absent)
- What is the cost of saying too much? (Social / Physical / Legal / None)
- What earns trust here? (Credential / Shared risk / Repeated interaction / Demonstrated skill)
- What is the cultural mode of emotional expression? (Understatement / Direct / Suppressed / Public)
Write a one-sentence register description. This becomes speech.register in the RON.
Examples:
| Culture | Register |
|---|---|
| Van Maanen's Star | direct, minimal pleasantries, gets to the point |
| Vael | complete sentences, institutional framing, measured, precise |
| Osse | terse, functional, equipment vocabulary, economy of language |
Step 3: Author the naming conventions (15 minutes)
Naming conventions follow from Reach position + settlement age + economic base:
| Parameter | Naming tendency |
|---|---|
| Core, old settlement | Polysyllabic, family-name-primary, formal phonemes |
| Mid-Reach, mature | Mixed — 1-2 syllables, either name primary depending on sub-culture |
| Frontier, new | Short, work-derived, first-name-only or work-name |
| Extraction industry | Often functional; legal names exist but aren't used |
| Administrative | Family names primary; given names reserved |
IP check rule: All names must be checked against:
- Hamilton (Contemporary English given names — avoid)
- Banks (Whimsical/elaborate — avoid)
- Reynolds Firefly (Earth-ethnic echoes — avoid)
- Simmons (Greek/Latin — avoid overt register matches)
Generate 25-35 given names and 15-20 family names. The generator needs variety across playthroughs.
Step 4: Author the voice persona (60 minutes — the core work)
The voice_persona field is the LLM instruction that shapes all re-voiced output for NPCs from this culture. This is where one working day of authoring matters most.
Structure (6-block format, ~200-250 tokens total)
PERSONA: You are a [Culture] [role type].
[BLOCK 1 — REGISTER, ~25 tokens]
State the register style, why it exists, one distinguishing marker.
[BLOCK 2 — CULTURAL CONTEXT, ~25 tokens]
One sentence: what shaped this culture's voice. The social or environmental fact.
[BLOCK 3 — VOCABULARY, ~40 tokens]
Exclamations: [closed list — ONLY these expressions. No invented variants.]
Greetings: [closed list]
Address conventions: [family-name-primary, first-name-only, etc.]
[BLOCK 4 — VALUES, ~20 tokens]
Two core values as behavioral instructions. "You do X." not "This culture values X."
[BLOCK 5 — NOT-LIST, ~20 tokens]
2-3 exclusions SPECIFIC to this culture. (Universal NIs cover global constraints.)
Start with: "Do not use..."
[BLOCK 6 — EXAMPLE PAIRS (~70-80 tokens): go in voice_examples field, not here]
The NEVER: block — most important element
Format: The NEVER: block is a separate, explicitly labeled section at the END of the persona string. It must not be embedded as a numbered instruction.
[positive instructions 1-8]\n\nNEVER: [3-5 explicit negatives defining what this culture does NOT sound like]
Why it matters: Spike 2 finding — explicit NEVER: blocks are critical for voice quality. Without them, the LLM defaults to register-neutral generic output. The numbered instructions alone are insufficient. The NEVER: section must be visually and structurally distinct — not "8. Do not use..." but a labeled block after the numbered list.
The not-list prevents register leakage. Write it by asking: what does this culture NEVER sound like?
| Culture | NEVER block content |
|---|---|
| Van Maanen's Star | Corporate/institutional language, effusive or polished register, deference to unearned rank, family names in casual use, verbosity |
| Vael | Void-adjacent exclamations (frontier register), casual contractions, sentence fragments, direct emotional expression, physical labor as value ground |
| Osse | Institutional framing or courtesy protocols, formal complete sentences, Commission-world vocabulary, Core-system exclamations, elaboration where a number suffices |
The NEVER: content must be culture-specific. Global constraints (no religious language, no Earth references) go in the universal RULES const — do not repeat them per-culture.
Writing the persona instructions
Use second-person imperative: "Be direct." not "This culture is direct."
Each numbered instruction should target a DIFFERENT speech dimension so trait modifiers can stack cleanly:
- Instruction 1 → delivery style (brevity, formality)
- Instruction 2 → cultural context (why they speak this way)
- Instruction 3 → address conventions (name usage)
- Instruction 4 → emotional register (how feelings appear in speech)
- Instruction 5 → institutional framing (how authority is referenced)
- Instruction 6 → exclamation vocabulary (closed, explicit list)
- Instruction 7 → trust signal (how the culture verifies credibility)
- Instruction 8 → not-list (what this voice never does)
Test before committing: Read the persona out loud. Can you imagine an NPC responding to you from this profile? Would a speaker from this culture be clearly distinct from Van Maanen's Star if you heard both without labels?
Step 5: Author voice examples (20 minutes)
Write 3-5 (input, output) example pairs for the voice_examples array.
Input: A culture-neutral semantic description of an action (same as base text format) Output: That action rendered in the culture's voice
Example pairs are the model's pattern anchors — small LLMs are pattern matchers before instruction-followers. The examples matter as much as the instructions.
Rules for example pairs:
- Cover the range: neutral, positive relationship, refusal/deflection, high-affect
- Output should be short (1-2 sentences). Long outputs lose the pattern.
- Each output must be recognizably THIS culture — read them side by side with Van Maanen's Star examples. They should sound like different people.
- Do not use real NPC names in examples — use generic role references
Cross-cultural contrast test
Before finalizing examples, run this test. Take the same three base-text inputs and write outputs in Van Maanen's Star, Vael, and Osse voices. If the outputs are hard to distinguish, the culture persona needs sharpening.
Example inputs for the test:
- "declines to answer a question about an overnight run"
- "acknowledges a colleague's greeting while continuing to work"
- "thanks a colleague for covering a shift"
| Input | Van Maanen's Star | Vael | Osse |
|---|---|---|---|
| Declines question | "Look, that's not mine to say." | "I'm not in a position to speak to that. You'd want to refer to the relevant operations log." | "Not my section. Ask Grek." |
| Acknowledges greeting | "Hey. Yeah. Catch you at shift end." | "Harthen. Good. I'll be with you in a moment." | "Doss. Yeah." |
| Thanks colleague | "Appreciated. See you at handoff." | "That was well handled. I've noted it." | "Solid. Owe you one." |
If all three rows sound similar, the culture personas need work.
Step 6: Author occasional injections (15 minutes)
occasional_injections are cultural vocabulary or speech patterns that should appear occasionally but not always — oath vocabulary, institutional references, equipment ground truth. The composition engine handles frequency; the author specifies what and when.
Fields:
kind— a string identifier (e.g.,"oath","protocol_cite","equipment_ground")clause— the LLM instruction when this injection firesexample—Some((input, output))showing correct usage, orNonefrequency— float 0.0–1.0 (how often the composition engine fires this injection)suppress_on_tells— tells that should suppress this injection (e.g., oath suppressed when Guarded)
Frequency guidelines:
- 0.25 — Van Maanen's Star oath vocabulary (appears in ~1 in 4 high-affect contexts)
- 0.20 — Vael protocol citation (appears occasionally in procedural exchange)
- 0.15 — Osse equipment ground (appears in work-adjacent contexts)
Start with one injection per culture. More can be added after Spike validation confirms the base persona is clean.
Step 7: Author the tell-tone table (15 minutes)
Each culture profile should include a tell-tone table mapping each TellCategory to how that tell expresses in this culture's register. This table is not in the RON file — it's authoring documentation used when tell-context injectors are written.
Include this table in a comment block in the RON file header, or maintain it separately in the culture's authoring notes.
Format:
| TellCategory | How it reads in this culture |
|---|---|
| Nervous | [culture-specific description] |
| Angry | [culture-specific description] |
| Friendly | [culture-specific description] |
| Guarded | [culture-specific description] |
| RoutineDeviation | [culture-specific description] |
Design principle (from D-138): Humans are humans first. Universal baseline (phenomenon-class behavior) is always readable across cultures. Cultural flavor is conditional and additive — the LLM adds cultural texture only if it doesn't significantly change the information conveyed. Per-culture tell-tone tables are optional enrichment.
Step 8: Validate and commit
tooling/validate-ron content/global/culture-{id}.ron culture
A passing validation confirms:
- All required fields are present and correctly typed
- Trait names are valid enum values
- RON syntax is correct
After validation, the culture profile is ready for Spike 1 prompt testing. Human review of voiced output is required before the culture goes into baked content.
Tell-tone reference tables
Van Maanen's Star tell-tone table
| TellCategory | Van Maanen's Star-inflected tonal register |
|---|---|
| Nervous | Answers run shorter than usual. Eyes stay on task. Nothing's wrong — just things to do. |
| Guarded | Direct past the point of directness. Closes conversation paths fast without being unfriendly. |
| Friendly | One beat more than the exchange needed. A word of warmth lands casually, not performed. |
| Angry | Steady. Even. The kind of steady that takes effort. Not hostile — just flat in a way that doesn't feel natural for Van Maanen's Star. |
| RoutineDeviation | Unhurried. Unremarkably normal. Like nothing's worth noticing. |
Vael tell-tone table
| TellCategory | Vael-inflected tonal register |
|---|---|
| Nervous | Sentences become over-complete — more formal than the situation warrants. Qualifications multiply. "As I understand it" appears where it wasn't needed. |
| Guarded | Responses are brief and technically correct. Every question answered with only what was asked. Institutional framing appears even in informal exchange. |
| Friendly | Slightly less formal than baseline — a given name used where a family name would normally appear. The exchange closes with a word of genuine regard. |
| Angry | Register becomes precisely correct — almost pedantically so. The effort to maintain composure shows as over-precision. |
| RoutineDeviation | Procedures are cited where they wouldn't normally be. A person who deviates from routine anchors to institutional language as cover. |
Osse tell-tone table
| TellCategory | Osse-inflected tonal register |
|---|---|
| Nervous | Words get shorter. Already-minimal speech contracts to one-word answers and physical acknowledgment. The silence between words lengthens. |
| Guarded | Assessment framing appears: "Why do you need to know?" — stated or implied. Eye contact shifts from reading-the-room to reading-you specifically. |
| Friendly | Slightly more than the exchange needed — an extra beat, an unrequested detail, a "good shift" that lands with more weight. Not warm, just more. |
| Angry | Everything slows down. Deliberate. Responses accurate, closed, no opening for follow-up. |
| RoutineDeviation | The opposite of nervous: unusually conversational. When someone is off-routine on purpose, they over-normalize by saying more than usual. |
Checklist: culture profile complete?
- Five parameters documented (Reach Position, Economic, Age, Heritage Root, Institutions)
- Register statement authored and distinctive
- 25+ given names, 15+ family names, phoneme pattern consistent
family_name_used_sociallyaccurately reflects the culture- Voice persona: 8 numbered instructions, ~200-250 tokens
- Explicit
NEVER:block at end of persona string (separate from numbered instructions) - NEVER block content is culture-specific (not repeating universal constraints)
- Cross-cultural contrast test passed (Van Maanen's Star/Vael/Osse comparison)
- 3-5 voice examples authored, range covered
- At least 1 occasional injection with suppression logic
- Tell-tone table authored (5 TellCategories)
tooling/validate-ron content/global/culture-{id}.ron culturepasses
Addendum: Behavior Modifiers (#634)
Added Sprint 26. Applies to all culture files after the composable behavior engine (#633) ships.
What behavior_modifiers does
The composable behavior engine assembles observable behavior strings from two parts:
BehaviorPrimitive.action + BehaviorModifier.clause → assembled behavior string
Example:
- Action:
"moves freight containers" - Modifier:
"without wasted motion"(Van Maanen's Star,work_pace) - Result:
"moves freight containers without wasted motion"
A culture provides a pool of modifier clauses. When the engine assembles a behavior, it looks for a modifier whose category matches the primitive's modifier_hint string. If no match exists, it falls back to any modifier in the pool. If no modifiers exist at all, the action text is used as-is.
Modifier clauses must grammatically trail any action text. They are appended with a space. Test by mentally substituting different action verbs: does the clause still read naturally?
BehaviorContext: when to use each tag
Every BehaviorPrimitive carries a context field that gates when it is eligible for selection. The composition engine filters by context before modifier selection.
| Context | Use for | Example action |
|---|---|---|
OnShift |
Physical task execution, work-site behaviors, role-specific routines | "checks a manifest against a handheld scanner", "guides a freight container into position" |
OffDuty |
Break-room behaviors, personal downtime, non-work physical states | "slumps into a break room chair and stares at nothing", "unwraps a meal packet standing at the counter" |
Social |
Social-site behaviors (bars, cantinas, communal spaces) — may overlap with OffDuty but specifically at a social venue | "nurses a drink at the end of the bar", "trades short words with a neighbor" |
Any |
Behaviors valid in any context — ambient, universal physical states | "rubs the back of their neck", "pauses and looks at nothing for a moment" |
Decision rule:
- If the behavior only makes sense on a work site →
OnShift - If the behavior only makes sense in a break room or personal space →
OffDuty - If the behavior specifically happens at a social venue →
Social - If the behavior could appear anywhere →
Any - When unsure, prefer
Anyover a specific context — you can always narrow later
Modifier category vocabulary (the contract)
These seven categories define the interface between primitives (Mellanie, role-action templates) and modifiers (culture authors). A primitive's modifier_hint must use one of these strings for a culture modifier to match it.
| Category | What it inflects | Example modifier (Van Maanen's Star) |
|---|---|---|
work_pace |
Speed and intensity of physical task execution | "without wasted motion" |
physical_manner |
Body language and movement quality during tasks | "without looking up from the task" |
social_signal |
Brief acknowledgments made during or between tasks | "with a short nod" |
task_completion |
How tasks are finished, wrapped up, or handed off | "and moves on" |
environmental_scan |
Awareness or vigilance while in a space | "with one eye still on the floor" |
offduty_posture |
Body language and social stance when off-shift | "without making a show of it" |
authority_response |
Behavior when supervisors or authority figures are present | "at the same pace as before" |
The fallback is safe but less targeted. A primitive with no modifier_hint gets a random modifier from the full culture pool. This still produces culturally inflected output — just without category precision. Prefer leaving modifier_hint unset over using an incorrect category string.
Authoring behavior_modifiers for a new culture
Minimum: 2 modifiers for each of the 7 categories = 14 modifiers total. Fewer categories will still work (fallback to any), but full coverage ensures every action type gets the right cultural texture.
Process:
- For each category, ask: How does this culture DISTINCTIVELY express this dimension?
- Not "how does any person do this" but "what is the cultural tell for this culture specifically?"
- Write the clause as a short trailing phrase (4-8 words). Test: does it read naturally after "moves freight containers ___"?
- Avoid clauses that are too action-specific — they need to trail any verb, not just one type
- Run
tooling/validate-ron content/global/culture-{id}.ron cultureto confirm syntax
Three-culture contrast for modifiers (same as voice persona contrast test):
| Category | Van Maanen's Star | Vael | Osse |
|---|---|---|---|
| work_pace | "without wasted motion" | "with practiced deliberation" | "at extraction pace" |
| physical_manner | "without ceremony" | "with institutional correctness" | "tool already in hand" |
| social_signal | "with a short nod" | "with a measured acknowledgment" | "with a word or a look" |
| task_completion | "and moves on" | "with the relevant entry already updated" | "gear stowed before moving on" |
| environmental_scan | "with one eye still on the floor" | "aware of who is observing" | "pressure and exits both in range" |
| offduty_posture | "without making a show of it" | "maintaining composed reserve" | "with one ear still on the work floor" |
| authority_response | "at the same pace as before" | "with appropriate professional deference" | "without a change in pace" |
If your new culture's modifiers would fit in any of the existing columns above without sounding different — the culture needs more work before modifiers are authored.
Updated checklist
Add these items to the culture profile checklist:
behavior_modifiersarray populated with at least 2 clauses per category (14 minimum)- Each clause tested: grammatically trails "moves freight containers ___" and "checks a manifest ___"
- Three-culture contrast check: modifiers are clearly distinct from Van Maanen's Star, Vael, and Osse
tooling/validate-ronstill passes after adding modifiers
Addendum: Sprint 26, #634 (composable behavior content). Category vocabulary established jointly by Gestalt (culture modifiers) and Mellanie (behavior primitives). Add new categories only by agreement between both sides — unmatched categories silently fall back to any modifier.