docs(copy): add authoring guides for base text, cultures, and content structure
Three scalability-focused authoring guides for content production across 300+ systems: - base-text-authoring-guide.md: elevation checklist, quality test - culture-authoring-guide.md: 8-step workflow, modifier contract, cross-cultural contrast test, one-day-per-culture target - content-structure-canonical.md: directory template, surviving structure audit, culture RON field reference Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,150 @@
|
||||
# Base Text Authoring Guide — The Settled Reach
|
||||
**Ticket:** #645
|
||||
**Author:** Mellanie
|
||||
**Status:** canonical
|
||||
**Last updated:** 2026-03-13
|
||||
|
||||
---
|
||||
|
||||
## Purpose
|
||||
|
||||
Base text is the culture-neutral semantic layer that every NPC line must have. It serves two purposes simultaneously:
|
||||
|
||||
1. **LLM seed** — the culture injector uses it as input. The better the base text, the better the voiced output.
|
||||
2. **Fallback** — when "AI-Enhanced Dialogue" is off or pre-voicing hasn't caught up, the player sees the base text directly. It must be functional and readable on its own.
|
||||
|
||||
This guide establishes the quality bar for all base text and documents failure modes to avoid.
|
||||
|
||||
---
|
||||
|
||||
## The quality bar (three tests)
|
||||
|
||||
Before committing a base text line, ask three questions:
|
||||
|
||||
1. **Does this show a moment, not a category?**
|
||||
A category tells you *what kind of thing* the NPC does. A moment shows you *a specific instance* of doing it.
|
||||
- Category: `"tends crops in the field"` — you know the role, not the person
|
||||
- Moment: `"works back along a crop row with a hoe, stopping twice to pull weeds by the root before moving on"` — you see a specific action sequence
|
||||
|
||||
2. **Could you picture a specific person doing this?**
|
||||
If the line could appear in a stock photo caption, it needs specificity. Add the HOW or the small physical detail that anchors it.
|
||||
|
||||
3. **Would you be okay if this were the only text the player ever sees?**
|
||||
The voice pipeline may not be available. The base text is not a draft — it is the default experience for players below spec or with AI-Enhanced Dialogue disabled. Author it to that bar.
|
||||
|
||||
---
|
||||
|
||||
## Failure modes and fixes
|
||||
|
||||
### 1. Category verb (`"tends"`, `"checks"`, `"monitors"`, `"manages"`)
|
||||
|
||||
These describe a role, not an action. Replace with a physical verb sequence.
|
||||
|
||||
| Below bar | At bar |
|
||||
|---|---|
|
||||
| `"tends rows of low-growing crops"` | `"works back along a crop row with a hoe, stopping twice to pull weeds by the root"` |
|
||||
| `"checks seedling trays"` | `"crouches over seedling trays and pinches back a leggy stem that grew sideways"` |
|
||||
| `"monitors the loading floor"` | `"stands at the mezzanine rail watching a forklift angle into a tight bay"` |
|
||||
|
||||
Fix: replace the category verb with the specific physical action that *constitutes* the checking or tending.
|
||||
|
||||
### 2. Quality modifier instead of specific action
|
||||
|
||||
Phrases like `"with practiced ease"`, `"with no wasted motion"`, `"efficiently"` are editorial commentary. The reader is told a quality instead of shown it.
|
||||
|
||||
| Below bar | At bar |
|
||||
|---|---|
|
||||
| `"lifts a crate of produce onto a flatbed with practiced ease"` | `"lifts a crate of produce onto the flatbed in one motion and reaches for the next before it settles"` |
|
||||
| `"swaps a panel module with practiced speed and no wasted motion"` | `"swaps a panel module in under a minute, tools back in the pouch before the cover is latched"` |
|
||||
|
||||
Fix: remove the modifier. Show the efficiency through the action sequence itself — specificity creates the impression.
|
||||
|
||||
### 3. Universal gesture (no character information)
|
||||
|
||||
Some gestures apply to anyone anywhere — wiping sweat, yawning, stretching. These are placeholder movements. Replace with something that shows role, circumstance, or cultural habit.
|
||||
|
||||
| Below bar | At bar |
|
||||
|---|---|
|
||||
| `"wipes sweat from her forehead with the back of a gloved hand"` | `"pulls a glove off with her teeth to wipe her face, pulls it back on without looking"` |
|
||||
| `"stretches"` | `"straightens up and rolls her neck once before kneeling back down"` |
|
||||
|
||||
Fix: find the physical quirk that's specific to this role, this body, this situation.
|
||||
|
||||
### 4. "Corporate-speak" / institutional passive
|
||||
|
||||
Lines that sound like they belong in a procedure manual. The NPC is a person, not a process.
|
||||
|
||||
| Below bar | At bar |
|
||||
|---|---|
|
||||
| `"initiates task completion protocol"` | `"gets back to sorting the manifests"` |
|
||||
| `"performs routine inspection of the facility perimeter"` | `"walks the fence line at a measured, unhurried pace"` |
|
||||
|
||||
Fix: use plain present tense and a physical verb. If you can hear a robot saying it, rewrite it.
|
||||
|
||||
---
|
||||
|
||||
## Lines you must NOT change
|
||||
|
||||
**`Factual`-type lines** are protected. These are lines bearing:
|
||||
- Specific numbers or quantities: `"fourteen crates in bay seven"`
|
||||
- Causal chains: `"the pressure dropped when the coupling failed"`
|
||||
- Denial statements: `"I don't know anything about that"`
|
||||
|
||||
Factual lines bypass the LLM entirely (D-138: `ContentType::Factual`). The pipeline serves them as exact base text. Do not elevate them — precision is their quality.
|
||||
|
||||
If you're unsure whether a line is Factual: if it contains a number, a named quantity, a specific event chain, or a denial with named content, treat it as protected.
|
||||
|
||||
---
|
||||
|
||||
## Cultural neutrality requirement
|
||||
|
||||
Base text must not contain culture-specific vocabulary. The culture injectors add that layer at voicing time. Base text is the semantic skeleton.
|
||||
|
||||
**What to avoid:**
|
||||
- Krenn oath vocabulary: `"void take it"`, `"blood and void"` — these go in voice_examples, not base text
|
||||
- Culture-specific greetings or farewells
|
||||
- Slang that implies a particular register
|
||||
|
||||
**Test:** Could this base text be voiced convincingly in *any* culture? If the answer is yes, it's culturally neutral. If not, strip the culture-specific markers.
|
||||
|
||||
---
|
||||
|
||||
## Volume guidance
|
||||
|
||||
Each role should have 15–25 on-shift behaviors plus 5–10 off-shift / social site behaviors. This gives the generator enough variety across a session without repetition. Roles with narrower activity ranges (security, militia) can have fewer.
|
||||
|
||||
---
|
||||
|
||||
## Elevation checklist (per line)
|
||||
|
||||
Before committing, run through:
|
||||
|
||||
- [ ] Physical verb sequence, not category verb
|
||||
- [ ] No quality modifiers — show efficiency through action, not through adjective
|
||||
- [ ] Character information visible (not a universal gesture)
|
||||
- [ ] No institutional/passive language
|
||||
- [ ] Culturally neutral (no culture-specific vocabulary)
|
||||
- [ ] Not a Factual-type line (no numbers, denials, or causal chains to preserve)
|
||||
- [ ] Reads as a complete, legible observation on its own
|
||||
|
||||
---
|
||||
|
||||
## Examples from the Krenn zones (reference)
|
||||
|
||||
These lines passed elevation review (Sprint 26):
|
||||
|
||||
**Strong — moment-specific, role-grounded:**
|
||||
- `"kneels at the base of a struggling plant and parts the soil with two fingers"`
|
||||
- `"threads a wire through a conduit clip and bites the insulation back with her teeth"`
|
||||
- `"keeps a junior worker between herself and the inspection team as the inspectors pass through"`
|
||||
- `"trades a quick look with a colleague when the foreman walks by"`
|
||||
- `"leans back on a stool and tracks a browser who has checked the same display twice without committing"`
|
||||
|
||||
**Strong — off-shift texture:**
|
||||
- `"sits with boots off under the table, socked feet flat on the floor"`
|
||||
- `"refills a neighbor's cup from her own jug without being asked"`
|
||||
- `"buys a round for the table and returns to her seat before anyone can thank her"`
|
||||
|
||||
---
|
||||
|
||||
*Last review: Sprint 26, base text elevation pass (#645). Apply this standard to all new zone RON files.*
|
||||
@@ -0,0 +1,236 @@
|
||||
# Content Structure — Canonical Template (v0.2+)
|
||||
|
||||
**Created:** 2026-03-13 (Sprint 26, #656 cleanup)
|
||||
**Supersedes:** `content-directory-structure.md` v0.1 design spec (that doc describes the *removed* structure)
|
||||
|
||||
This document defines the canonical directory layout for campaign content after removal of v0.1 hand-authored dead weight. It is the template to replicate across 300 systems.
|
||||
|
||||
---
|
||||
|
||||
## Actual Directory Hierarchy
|
||||
|
||||
The real hierarchy (note: diverges from old design doc which used `content/districts/`):
|
||||
|
||||
```
|
||||
content/
|
||||
global/ # Cross-system content
|
||||
culture-{name}.ron # Voice pipeline persona (LIVE GENERATOR INPUT)
|
||||
culture-{name}.example.ron # Example output for reference
|
||||
zone-identity-spec.example.ron # Zone identity template example
|
||||
factions/ # Faction profiles (one per faction)
|
||||
knowledge/ # Shared FactId catalog (one per category)
|
||||
enums/ # Shared enum definitions
|
||||
contraband/ # Contraband item profiles
|
||||
technology/ # Technology definitions
|
||||
|
||||
campaigns/
|
||||
main/
|
||||
systems/
|
||||
{system-slug}/ # e.g. krenn/
|
||||
system.yaml # System metadata
|
||||
stations/
|
||||
{station-slug}/ # e.g. sova/
|
||||
station.yaml # Station metadata
|
||||
districts/
|
||||
{district-slug}/ # e.g. transit/
|
||||
district.yaml # Zone metadata (CANONICAL METADATA)
|
||||
locations/ # Physical location definitions
|
||||
templates/ # Social site role-slot definitions
|
||||
triangles/ # Drama triangles (3-NPC clusters)
|
||||
environmental/ # Environmental text (signs, terminals, docs)
|
||||
routines/ # NPC schedule definitions
|
||||
ticker/ # News ticker content
|
||||
|
||||
modules/
|
||||
tier1/ # Tier 1 drama modules (authored)
|
||||
{module-slug}.yaml
|
||||
|
||||
gauntlet/ # Test world (immutable — do not modify)
|
||||
rooms/
|
||||
|
||||
_schema/ # JSON Schema validation files
|
||||
_meta/ # Infrastructure metadata
|
||||
schemas/ # (additional schemas)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## What Each Piece Is
|
||||
|
||||
| Path | Type | Who produces it | Notes |
|
||||
|------|------|-----------------|-------|
|
||||
| `global/culture-{name}.ron` | **Generator input** (authored) | Copy team | Voice pipeline persona. One per culture. DO NOT DELETE. |
|
||||
| `global/factions/*.yaml` | **Authored** | Copy team | Faction profiles. One per faction. |
|
||||
| `global/knowledge/*.yaml` | **Authored** | Copy team | FactId catalog by category. |
|
||||
| `global/enums/*.yaml` | **Authored** | Copy team | Shared enum definitions (situations, moods, etc.) |
|
||||
| `system.yaml` | **Metadata** | Copy team | System display name, factions present, setting notes. |
|
||||
| `station.yaml` | **Metadata** | Copy team | Station description, district list. |
|
||||
| `district.yaml` | **Metadata** | Copy team | Zone display name, location list, target NPC count. |
|
||||
| `locations/*.yaml` | **Metadata** | Copy team | One file per location: display name, description, type. |
|
||||
| `templates/*.yaml` | **Authored** | Copy team | Role slot definitions (what roles exist, capacity range). Survives to v0.2+. The `v01_assignments` field is v0.1-only — clear for generated content. |
|
||||
| `triangles/*.yaml` | **Authored** | Copy team | Drama triangles with fork conditions and resolution states. Template material for generated equivalents. |
|
||||
| `environmental/*.yaml` | **Authored** | Copy team | Environmental text objects with optional dual-lens properties (v0.1 remains). |
|
||||
| `routines/schedules.yaml` | **Authored** | Copy team | NPC daily routines. Will be generated per-NPC in v0.2+. |
|
||||
| `ticker/*.yaml` | **Authored** | Copy team | News ticker content for this district. |
|
||||
| `modules/tier1/*.yaml` | **Authored** | Copy team | Drama modules (Paula's schema). |
|
||||
|
||||
---
|
||||
|
||||
## What Was Removed (v0.1 Dead Weight)
|
||||
|
||||
These directories and files existed in v0.1 and have been deleted (Sprint 26, #656/#657):
|
||||
|
||||
| Removed | Reason |
|
||||
|---------|--------|
|
||||
| `npcs/*.yaml` | Hand-authored named NPC profiles. Replaced by generator output. All 23 files deleted. |
|
||||
| `dialogue/` | Hand-authored NPC dialogue pools per role/location. Replaced by voice pipeline. |
|
||||
| `monologue/detective/` | Detective player monologue. Detective archetype removed per D-117. |
|
||||
| `monologue/smuggler/` | Smuggler player monologue. Smuggler archetype removed per D-117. |
|
||||
| `insert/detective.yaml` | Detective insert HUD content. |
|
||||
| `insert/smuggler.yaml` | Smuggler insert HUD content. |
|
||||
| `items/smuggler-inventory.yaml` | Smuggler item definitions. |
|
||||
| `pools.yaml` | v0.1 NPC candidate pool definitions. All candidates referenced deleted NPCs. |
|
||||
| `content/global/knowledge/investigation.yaml` | Investigation facts. Detective system removed. (Deleted in #657.) |
|
||||
| `content/global/factions/lattice-commission.yaml` | Detective faction. (Deleted in #657.) |
|
||||
| Several `docs/design/` detective/smuggler docs | Archetype design docs. (Deleted in #657.) |
|
||||
|
||||
---
|
||||
|
||||
## Canonical Template: New System Checklist
|
||||
|
||||
When adding a new system for v0.2+ (target: 300 systems), replicate this pattern:
|
||||
|
||||
```
|
||||
content/campaigns/main/systems/{system-slug}/
|
||||
system.yaml # 1. Author system metadata
|
||||
stations/
|
||||
{station-slug}/
|
||||
station.yaml # 2. Author station metadata
|
||||
districts/
|
||||
{district-slug}/
|
||||
district.yaml # 3. Author district metadata
|
||||
locations/ # 4. Define locations (5-10 per district)
|
||||
{location-slug}.yaml
|
||||
templates/ # 5. Define social site templates (role slots)
|
||||
{template-slug}.yaml
|
||||
routines/ # 6. Author schedule structure
|
||||
schedules.yaml # (v0.2: generated per NPC)
|
||||
ticker/ # 7. Author news ticker content
|
||||
{location-slug}.yaml
|
||||
```
|
||||
|
||||
**Note:** `environmental/` and `triangles/` are authored for hand-crafted districts. For generated districts, these may be generated or omitted in v0.2+.
|
||||
|
||||
**No NPCs.** No dialogue. No monologue. Those are generator outputs, not authored inputs.
|
||||
|
||||
---
|
||||
|
||||
## Culture RON File Reference
|
||||
|
||||
Culture files are the primary input for the voice pipeline. One file per culture, living at `content/global/culture-{name}.ron`.
|
||||
|
||||
**Use `content/global/culture-krenn.ron` as the authoring template — not `culture-krenn.example.ron`.** The example file is an older stub without voice pipeline fields.
|
||||
|
||||
### Required fields (must be present)
|
||||
|
||||
```
|
||||
id: "slug" # kebab-case, matches filename
|
||||
name: "Display Name Culture"
|
||||
description: "Single sentence: settlement age, location type, economic base, social character"
|
||||
|
||||
naming: (
|
||||
style: # prose: phoneme style, casing norms
|
||||
given_names: [...] # 30-50 names minimum for adequate generator variety
|
||||
family_names: [...] # 10-20 names
|
||||
family_name_used_socially: bool
|
||||
)
|
||||
|
||||
speech: (
|
||||
register: # prose: formality, rhythm, directness
|
||||
filler_words: [...] # 4-8 words
|
||||
greetings: [...] # 3-6 phrases
|
||||
farewells: [...] # 3-6 phrases
|
||||
exclamations: [...] # 4-8 oath/exclamation phrases
|
||||
)
|
||||
|
||||
values: (
|
||||
description: # prose: core value system
|
||||
favored_traits: [...] # subset of valid traits (see below)
|
||||
disfavored_traits: [...] # subset of valid traits
|
||||
)
|
||||
```
|
||||
|
||||
### Optional fields (omit or include as needed)
|
||||
|
||||
```
|
||||
voice_persona: Some("PERSONA: ...") # Full LLM prompt persona block
|
||||
voice_examples: [ # 3-5 input/output pairs
|
||||
(input: "...", output: "..."),
|
||||
]
|
||||
occasional_injections: [ # Rolled per-prompt at given frequency
|
||||
(
|
||||
kind: "oath" # human-readable category label
|
||||
clause: "When ... use ...", # LLM instruction text
|
||||
example: Some((input: "...", output: "...")), # optional
|
||||
frequency: 0.25, # 0.0–1.0
|
||||
suppress_on_tells: [Guarded], # optional: omit injection when these tells active
|
||||
),
|
||||
]
|
||||
behavior_modifiers: [ # Culture-specific action modifier clauses (#633)
|
||||
(category: "work_style", clause: "with mechanical efficiency"),
|
||||
]
|
||||
```
|
||||
|
||||
### Valid enum values
|
||||
|
||||
**PersonalityTrait** (for `favored_traits` / `disfavored_traits`):
|
||||
`Cautious, Bold, Honest, Deceptive, Compassionate, Ruthless, Curious, Incurious, Social, Reclusive`
|
||||
|
||||
**TellCategory** (for `suppress_on_tells`):
|
||||
`Nervous, Angry, Friendly, Guarded, RoutineDeviation`
|
||||
|
||||
### voice_persona: NOT-list requirement (D-138, Spike 2)
|
||||
|
||||
Spike 2 found that **explicit NOT-lists in the persona block are critical** for voice quality. Without them, the LLM defaults to register-neutral generic output. Add an explicit "NEVER:" or "NOT:" section at the end of the persona block:
|
||||
|
||||
```
|
||||
voice_persona: Some(
|
||||
"PERSONA: ...\n
|
||||
[positive instructions]\n\n
|
||||
NEVER: [list 3-5 explicit negatives that define what this culture does NOT sound like]"
|
||||
),
|
||||
```
|
||||
|
||||
Example negatives for Krenn (currently implicit, should be made explicit in a future pass):
|
||||
- Never effusive, never corporate-polished, never deferential to rank
|
||||
- Never flowery or verbose
|
||||
|
||||
`culture-krenn.ron` does not yet have an explicit NOT-list — this is a known gap to address when doing a persona quality pass.
|
||||
|
||||
### behavior_modifiers: status
|
||||
|
||||
`culture-krenn.ron` does not populate `behavior_modifiers` — it defaults to `[]`. This field is for the decomposed behavior system (#633/#634), which is not yet shipped. Leave empty for all cultures until #634 format is confirmed.
|
||||
|
||||
---
|
||||
|
||||
## Scale Readiness Audit
|
||||
|
||||
### Passes at 300 systems
|
||||
|
||||
| Structure | Ready | Notes |
|
||||
|-----------|-------|-------|
|
||||
| `content/campaigns/main/systems/` | Yes | One directory per system. No monolithic files. |
|
||||
| `content/global/factions/` | Yes | One file per faction — will grow to ~20-50 factions across 300 systems |
|
||||
| `content/global/knowledge/` | Mostly | Knowledge catalog is organized by category, not by system. Flat list works to ~500 facts. |
|
||||
| `content/global/culture-{name}.ron` | Yes | One file per culture (expected ~10-20 cultures total). |
|
||||
| `templates/*.yaml` | Yes | Role slot definitions are reusable across systems of the same type. |
|
||||
| `modules/tier1/` | Yes | Flat directory. Drama modules are self-contained. |
|
||||
|
||||
### Needs attention at scale
|
||||
|
||||
| Structure | Issue | Recommendation |
|
||||
|-----------|-------|----------------|
|
||||
| `content/global/knowledge/investigation.yaml` | **Deleted** — was detective-system-only. Other knowledge files need review for detective/smuggler references. | Audit surviving knowledge files post-#657. |
|
||||
| `environmental/*.yaml` | Has v0.1 `dual_lens` (smuggler/detective) properties in the terminal/bar/corridors files. These are dead properties once archtypes are removed. | Flag to client team — strip `dual_lens` field from environmental schema for v0.2. |
|
||||
| `templates/` v01_assignments | Fields reference deleted named NPCs. Not validated but stale. | Remove `v01_assignments` and `reference_links` from templates when generating v0.2+ content. |
|
||||
| `triangles/` members | Reference deleted named NPCs (e.g., `npc:voss`, `npc:kael-davan`). These are unresolvable after NPC deletion. | These triangles are v0.1 examples. Either delete or annotate as templates for generated equivalents. |
|
||||
@@ -0,0 +1,385 @@
|
||||
# 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-krenn.ron` — Mid-Reach, freight, working-class pragmatic (the Spike 1/2 reference)
|
||||
- `culture-vael.ron` — Core-adjacent, administrative/research, institutional-formal
|
||||
- `culture-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`:
|
||||
|
||||
1. **Reach Position:** Core / Mid-Reach / Frontier / Deep Frontier
|
||||
→ Determines authority attitude, institutional density, Commission presence
|
||||
|
||||
2. **Economic Base:** Freight / Extraction / Manufacturing / Service / Research / Mixed
|
||||
→ Determines daily vocabulary, rhythms, what NPCs care about
|
||||
|
||||
3. **Settlement Age:** New (< 20 yr) / Established (20-80 yr) / Mature (80-200 yr) / Old (200+ yr)
|
||||
→ Determines rootedness, architectural character, whether "here" is home
|
||||
|
||||
4. **Heritage Root:** Frost / Tide / Iron / Dust / Stone / Vine / Salt
|
||||
→ Determines spatial grammar and social privacy model (D-104/D-105)
|
||||
|
||||
5. **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 |
|
||||
|---|---|
|
||||
| Krenn | 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 |
|
||||
|---|---|
|
||||
| Krenn | 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 Krenn 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:**
|
||||
1. Cover the range: neutral, positive relationship, refusal/deflection, high-affect
|
||||
2. Output should be short (1-2 sentences). Long outputs lose the pattern.
|
||||
3. Each output must be recognizably THIS culture — read them side by side with Krenn examples. They should sound like different people.
|
||||
4. 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 Krenn, 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 | Krenn | 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 fires
|
||||
- `example` — `Some((input, output))` showing correct usage, or `None`
|
||||
- `frequency` — 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 — Krenn 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
|
||||
|
||||
```bash
|
||||
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
|
||||
|
||||
### Krenn tell-tone table
|
||||
|
||||
| TellCategory | Krenn-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 Krenn. |
|
||||
| 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_socially` accurately 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 (Krenn/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 culture` passes
|
||||
|
||||
---
|
||||
|
||||
---
|
||||
|
||||
## 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"` (Krenn, `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 `Any` over 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 (Krenn) |
|
||||
|---|---|---|
|
||||
| `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:**
|
||||
1. 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?"
|
||||
2. Write the clause as a short trailing phrase (4-8 words). Test: does it read naturally after "moves freight containers ___"?
|
||||
3. Avoid clauses that are too action-specific — they need to trail any verb, not just one type
|
||||
4. Run `tooling/validate-ron content/global/culture-{id}.ron culture` to confirm syntax
|
||||
|
||||
**Three-culture contrast for modifiers** (same as voice persona contrast test):
|
||||
|
||||
| Category | Krenn | 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_modifiers` array 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 Krenn, Vael, and Osse
|
||||
- [ ] `tooling/validate-ron` still 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.*
|
||||
Reference in New Issue
Block a user