Files
settled-reach/wiki/authoring/monologue-guide.md
T
jpmschweitzerandClaude Opus 4.6 b672b9c269 feat(docs): restructure wiki taxonomy with templates and Krenn System entry (#301, #319)
Move wiki from docs/wiki/ to root wiki/. Add 14 category templates with
YAML frontmatter, governance model, hierarchical spatial structure
(system > station > district > location). Populate Krenn System entry
as first concrete star system. Update cross-references in workshop and
sprint docs.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-13 00:58:45 +01:00

1362 lines
44 KiB
Markdown

---
title: "Monologue Authoring Guide"
slug: monologue-guide
category: authoring-guide
status: canonical
created: 2026-02-12
updated: 2026-02-13
tags: []
decision_refs: [D-032, D-035]
cross_refs: []
---
# Monologue and Dialogue Authoring Guide
How to write tagged monologue and dialogue content for The Settled Reach. Read the knowledge system docs first — this guide assumes you know what a FactId is and how RelationshipState works.
**Prerequisites:**
- [Fact Catalog](../knowledge/fact-catalog.md) — 24 FactIds with confidence progressions
- [Entity Attributes](../knowledge/entity-attributes.md) — 14 canonical EntityKnowledge keys
- [Relationship States](../knowledge/relationship-states.md) — 5 states, transitions, dialogue access tiers
**Canonical sources:** D-028 (dialogue architecture), D-032 (separate monologue pools), D-035 (tag taxonomy)
---
## Location Shortcodes
Canonical shortcodes for v0.1 Sova Transit District. Used in line IDs (`{shortcode}_{type}_{###}`) and file naming.
| Shortcode | Location | Full Name | D-036 Reference |
|-----------|----------|-----------|-----------------|
| `terminal` | Logistics Hub | The Terminal | Primary workplace social site |
| `bar` | The Last Shift | Lera's (informal) | Social venue |
| `corridor` | Maintenance corridors | Corridor B-7 (primary) | Smuggling/service spaces |
**File naming convention:** `{shortcode}-{character}.yaml` (e.g., `terminal-smuggler.yaml`, `bar-detective.yaml`)
**Line ID format:** `{shortcode}_{d|m|e}_{###}` where `d`=dialogue, `m`=monologue, `e`=environmental
---
## Situation Overlap Rules
A line can have multiple situations in its `situation` list. These rules define valid combinations.
**Mutually exclusive** (never co-occur):
- `arrival` and `shift_end` — you can't be both arriving and leaving
- `alone` and `social` — by definition
- `emergency` and `routine` — emergency overrides routine
- `confrontation` and `social` — confrontation replaces social context
**Common valid overlaps:**
- `shift_start` + `routine` — beginning-of-shift routine observations
- `bar_evening` + `social` — social interaction at the bar
- `observation` + any non-exclusive situation — observation layers on top of context
- `investigation` + `observation` — investigating by watching
**Default if empty:** `routine` (engine assumes routine context)
---
## Mood Exclusivity Rules
A line can have multiple moods, but some combinations are contradictory.
**Mutually exclusive** (never co-occur):
- `fond` and `suspicious` — these are opposing orientations toward someone
- `comfortable` and `worried` — opposing emotional states
- `relieved` and `concerned` — resolved vs. unresolved tension
**Common valid overlaps:**
- `analytical` + any mood — analytical is a cognitive mode that coexists with emotion
- `conflicted` + `fond` or `concerned` — conflicted implies mixed feelings
- `worried` + `suspicious` — worry about someone's behavior shading into suspicion
**Default if empty:** `comfortable` (engine assumes neutral emotional baseline)
---
## Core Principle
Internal monologue is **hard-partitioned by playable character** (D-032). The smuggler and detective have completely separate monologue pools. No shared lines. Same trigger, different pool. This is how mirror moments work without either pool knowing about the other.
Dialogue lines are shared — NPCs say the same words regardless of who's listening. The access tier system filters which lines each character can hear at runtime.
---
## D-035 Tag Taxonomy Reference
Every line carries structural tags, selection tags, and (for monologue) character-specific tags. Here's the full schema.
### Structural Tags (required on every dialogue line)
| Tag | Type | Purpose |
|-----|------|---------|
| `id` | string | `{location}_{d\|m\|e}_{###}` — stable, machine-parseable |
| `text` | string | The authored line |
| `role` | enum | Template-defined role, not NPC name (NPC assignment is runtime) |
| `access` | list\<enum\> | Hard filter: `public`, `insider`, `authority`, `peer`, `hostile` |
| `trust` | enum | Hard filter: `surface`, `real`, `secret` |
| `situation` | list\<enum\> | Context filter (13 situations for v0.1) |
### Selection Tags (recommended, defaults apply if empty)
| Tag | Type | Purpose |
|-----|------|---------|
| `topic` | list\<enum\> | Weighted selection (9 topics for v0.1) |
| `mood` | list\<enum\> | Weighted selection (8 moods for v0.1) |
| `tags` | list\<string\> | Freeform escape hatch for author intent |
### Monologue-Specific Tags (added to structural + selection)
| Tag | Type | Purpose |
|-----|------|---------|
| `character` | enum | `smuggler` or `detective` — hard partition (D-032) |
| `trigger` | enum | What fires this line (9 types — see next section) |
| `prerequisite` | map or null | Knowledge state gate |
### Authoring-Only Tags (not consumed by engine)
| Tag | Type | Purpose |
|-----|------|---------|
| `dual_lens` | map | Per-character notes on how the line reads differently |
| `notes` | string | Author intent, context for future editors |
### Enum Values
**`situation`** (13 for v0.1):
`arrival`, `shift_start`, `shift_end`, `shift_transition`, `bar_evening`, `night_shift`, `investigation`, `confrontation`, `social`, `alone`, `emergency`, `routine`, `observation`
**`topic`** (9 for v0.1):
`colleague`, `routine`, `cargo`, `money`, `trust`, `danger`, `institution`, `personal`, `investigation`
Note: no `crime` or `smuggling` topic. NPCs think of it as `cargo` or `money`. The detective uses `investigation`.
**`mood`** (8 for v0.1):
`fond`, `comfortable`, `worried`, `suspicious`, `analytical`, `conflicted`, `concerned`, `relieved`
---
## Monologue Trigger Types (9)
The `trigger` tag determines WHEN a monologue line fires. Each type maps to a game event.
### 1. `enter_location`
Fires when the player enters a named area.
```yaml
- id: terminal_m_010
text: "Morning shift. Recycled air and cargo lubricant. Home sweet home."
character: smuggler
trigger: enter_location
situation: [arrival]
mood: [comfortable]
prerequisite: null
```
```yaml
- id: terminal_m_011
text: "Logistics hub. Standard prefab, heavy foot traffic. Let's see what the shift change tells me."
character: detective
trigger: enter_location
situation: [arrival]
mood: [analytical]
prerequisite: null
```
**Mirror moment**: Same entry, different register. Smuggler sees home; detective sees a crime scene.
Use for first impressions, atmosphere, returning-to-familiar-ground observations. Keep short — the player is moving.
---
### 2. `observe_npc`
Fires when a known entity enters the player's line of sight.
```yaml
- id: terminal_m_020
text: "Kael's here. Good — I was starting to worry."
character: smuggler
trigger: observe_npc
mood: [fond]
prerequisite:
relationship_state: Friendly
subject: kael_davan
```
```yaml
- id: terminal_m_021
text: "Dock worker from the case file. Davan, K. Unremarkable on paper."
character: detective
trigger: observe_npc
mood: [analytical]
prerequisite:
fact: investigation.manifest_discrepancy
confidence: Suspects
```
Use for identification, behavioral observations, emotional reactions. The prerequisite controls which version fires — same NPC, different line depending on knowledge.
---
### 3. `hear_sound`
Fires on audio events — ambient, diegetic, or anomalous.
```yaml
- id: bar_m_030
text: "Raised voices from the back room. Devra's in there. That's not a brokering call."
character: smuggler
trigger: hear_sound
mood: [concerned]
prerequisite:
fact: contraband.ring_exists
confidence: KnowsDetails
```
```yaml
- id: bar_m_031
text: "Arguing behind that door. The bartender didn't flinch. Either she's used to it or she knows what it is."
character: detective
trigger: hear_sound
mood: [suspicious]
prerequisite: null
```
Audio triggers fire less frequently than visual ones. Make them count.
---
### 4. `observe_anomaly`
Fires when the player observes something that contradicts expected state — a container in the wrong place, an NPC where they shouldn't be, a door that should be locked.
```yaml
- id: terminal_m_040
text: "Kael's checking his lattice again. Third time in ten minutes. That's not normal."
character: smuggler
trigger: observe_anomaly
mood: [worried]
prerequisite:
relationship_state: Friendly
subject: kael_davan
```
```yaml
- id: terminal_m_041
text: "Dock worker Davan — lattice activity spiked. Expecting a message? Or checking for surveillance?"
character: detective
trigger: observe_anomaly
mood: [analytical]
prerequisite:
known_attributes:
subject: kael_davan
key: behavior_flags
value_contains: lattice_checking
```
This is the detective's bread and butter: the gap between expected and observed.
---
### 5. `post_conversation`
Fires after a dialogue exchange ends. Internal reaction to what just happened.
```yaml
- id: bar_m_050
text: "Sera's good to have around. Knows the district, knows the people. Reliable."
character: detective
trigger: post_conversation
mood: [fond]
prerequisite:
relationship_state: Friendly
subject: sera_venn
```
```yaml
- id: bar_m_051
text: "She's not lying. But she's not telling me everything. Sera, what do you know?"
character: detective
trigger: post_conversation
mood: [conflicted]
prerequisite:
relationship_state: PersonOfInterest
subject: sera_venn
```
This is where character voice shines — the detective analyzes, the smuggler worries about people.
---
### 6. `discover_evidence`
Fires when the player finds physical evidence — a document, object, or data point.
```yaml
- id: terminal_m_060
text: "Container 4471. Manifest says 240 kilos. That crate is closer to 310."
character: detective
trigger: discover_evidence
mood: [analytical]
prerequisite:
fact: investigation.manifest_discrepancy
confidence: Suspects
```
```yaml
- id: terminal_m_061
text: "4471's still in temp. Kael routed it there during transition. Standard. Nothing to worry about."
character: smuggler
trigger: discover_evidence
mood: [relieved]
prerequisite:
fact: contraband.ring_exists
confidence: KnowsDetails
```
Brief, observational. The fact system handles what the player now knows — the monologue handles what they feel about it.
---
### 7. `witness_interaction`
Fires when the player observes two NPCs interacting.
```yaml
- id: bar_m_070
text: "Sera's headed out. Smart — Torek gets loud after three drinks. Nobody wants to deal with that."
character: smuggler
trigger: witness_interaction
mood: [comfortable]
prerequisite: null
```
```yaml
- id: bar_m_071
text: "That's twice now. Sera leaves when Torek arrives. Coincidence? Filing it."
character: detective
trigger: witness_interaction
mood: [analytical]
prerequisite:
known_attributes:
subject: sera_venn
key: behavior_flags
value_contains: avoidance_pattern
```
**Mirror moment**: Same event. Smuggler has the innocent explanation. Detective has the investigative read. On replay: "Wait — WAS it just the noise?"
---
### 8. `time_idle`
Fires after inactivity — the player is standing still, watching, waiting. The character's mind wanders.
```yaml
- id: corr_m_080
text: "Quiet tonight. Good. Nobody using the south access."
character: smuggler
trigger: time_idle
mood: [relieved]
prerequisite:
fact: location.corridor_b7_restricted
confidence: KnowsDetails
```
```yaml
- id: corr_m_081
text: "This corridor should see foot traffic during shift change. It's empty. Why?"
character: detective
trigger: time_idle
mood: [suspicious]
prerequisite:
fact: location.corridor_b7_restricted
confidence: Suspects
```
Idle lines reveal what the character cares about when nothing demands attention. The smuggler thinks about the operation; the detective thinks about the case.
---
### 9. `return_visit`
Fires when the player returns to a location they've visited before. Distinct from `enter_location` — gates on prior visit history.
```yaml
- id: bar_m_090
text: "Back at Lera's. Same seats, same faces. Sera's in the corner booth. Good."
character: detective
trigger: return_visit
mood: [comfortable]
prerequisite:
relationship_state: Friendly
subject: sera_venn
```
```yaml
- id: bar_m_091
text: "Back again. Same booth. ...Let's see if Sera leaves when Torek shows up."
character: detective
trigger: return_visit
mood: [suspicious]
prerequisite:
known_attributes:
subject: sera_venn
key: behavior_flags
value_contains: avoidance_pattern
```
Return visits build investigation momentum. This is where the detective's pattern recognition fires.
---
## YAML Prerequisite Format
Prerequisites gate monologue lines on knowledge state. A line only fires if the player's knowledge meets the conditions.
### No prerequisite (always eligible)
```yaml
prerequisite: null
```
Still filtered by trigger type, character, and location. Use for baseline observations that don't depend on knowledge.
### Gating on FactId + confidence
```yaml
prerequisite:
fact: contraband.ring_exists
confidence: KnowsOf
```
Fires only if the active character knows the named fact at the specified confidence **or higher**. Confidence levels ordered: `Suspects` < `KnowsOf` < `KnowsDetails` < `Direct`.
Use exact FactId strings from the [Fact Catalog](../knowledge/fact-catalog.md) — `"contraband.ring_exists"` not `"Ring Exists"`. The 24 v0.1 FactIds are the only valid values.
### Gating on RelationshipState
```yaml
prerequisite:
relationship_state: PersonOfInterest
subject: kael_davan
```
Fires only if the active character's relationship with the named entity is the specified state. States: `Unknown`, `Known`, `Friendly`, `PersonOfInterest`, `Hostile`.
### Gating on EntityKnowledge attributes
**Exact match:**
```yaml
prerequisite:
known_attributes:
subject: kael_davan
key: contradiction_flagged
value: meeting_unknown_contact
```
Fires if the active character's `EntityKnowledge` for the target has the exact key-value pair.
**Partial match** (for comma-separated fields like `behavior_flags`):
```yaml
prerequisite:
known_attributes:
subject: kael_davan
key: behavior_flags
value_contains: lattice_checking
```
Matches if `behavior_flags` contains `lattice_checking` anywhere in the comma-separated list.
### Compound prerequisites
Combine conditions with `all` (AND) or `any` (OR):
```yaml
prerequisite:
all:
- fact: contraband.ring_exists
confidence: KnowsDetails
- relationship_state: Friendly
subject: kael_davan
- known_attributes:
subject: kael_davan
key: behavior_flags
value_contains: lattice_checking
```
All three must be true. Swap `all` for `any` for OR logic.
Keep compound prerequisites to 2-3 conditions. If you need more, the line is too narrowly targeted — split it.
### Character-specific gating
The `character` tag on monologue lines is already a hard partition. You don't need to gate on character inside `prerequisite`. But some facts are character-specific in practice:
```yaml
# Detective-only fact — smuggler never reaches this
prerequisite:
fact: investigation.evidence_collected
confidence: KnowsOf
```
```yaml
# Both characters can reach this, but from different starting points
prerequisite:
fact: contraband.ring_exists
confidence: KnowsOf
# Smuggler starts here. Detective earns it through investigation.
```
---
## Access Tier Mapping
The `access` tag on dialogue lines determines who can hear what. Access is derived from `RelationshipState` plus character type (smuggler/detective).
### RelationshipState to dialogue access
| `access` tag | Unknown | Known | Friendly | PersonOfInterest | Hostile |
|--------------|---------|-------|----------|------------------|---------|
| `public` | yes | yes | yes | yes | yes |
| `peer` | -- | yes | yes | -- | -- |
| `insider` | -- | -- | yes | -- | -- |
| `authority` | detective | detective | -- | detective | -- |
| `hostile` | -- | -- | -- | -- | yes |
**Key rules:**
- **`public`**: Anyone can hear it. Greetings, small talk, general information.
- **`peer`**: Requires Known or Friendly. Coworker talk, casual gossip. **Locks out at PersonOfInterest** — when trust breaks, peers stop sharing.
- **`insider`**: Requires Friendly only. Ring operations talk, close confidences. The content that vanishes when THE FRIEND turns amber.
- **`authority`**: Detective-only, works at Unknown, Known, and PersonOfInterest. Institutional leverage. Doesn't work on Friendly (too personal) or Hostile (they've stopped cooperating).
- **`hostile`**: Only at Hostile. Threats, demands, final warnings.
### What this means for THE FRIEND
When Kael transitions from Friendly to PersonOfInterest:
- `insider` lines disappear. Ring talk stops. Trust-gated confidences lock.
- `peer` lines disappear. Casual warmth dries up. Kael becomes guarded.
- `public` lines remain. Kael still greets the smuggler — but the warmth is gone.
When Sera transitions from Friendly to PersonOfInterest:
- `insider` lines disappear. Institutional background sharing stops.
- `peer` lines disappear. Bar conversation becomes stilted.
- `authority` lines unlock for the detective. Pressing questions become available.
This is the mechanical expression of the emotional arc. The content doesn't change — the **filter** changes. Lines that existed before are now locked. That absence IS the narrative.
### Trust sublayers
Within each access tier, `trust` further filters content:
| `trust` | What it gates |
|---------|--------------|
| `surface` | Safe topics, social norms, what anyone would say |
| `real` | Honest opinions, unguarded speech, actual feelings |
| `secret` | Dangerous knowledge, confessions, things that create leverage |
Trust is orthogonal to access. A `peer` + `surface` line is casual coworker talk. A `peer` + `real` line is a coworker dropping the mask. A `peer` + `secret` line is a coworker confessing something risky.
### Per-NPC access mapping
How each character relates to key NPCs:
| NPC | Smuggler Access | Detective Access |
|-----|----------------|------------------|
| Kael (THE FRIEND) | `insider`, `peer` | `public``authority` (after flagging) |
| Sera (THE FRIEND) | `public` (Commission, wary) | `peer``authority` (post-contradiction) |
| Voss | `insider` (colleague) | `authority` |
| Lera | `insider` (bar regular) | `public``peer` (repeat visits) |
| Naia | `peer` (through Kael) | `public``peer` (through Sera) |
| Torek | `insider` or `peer` | `public` |
| Maret | `insider` (hub colleague) | `authority` |
| Drin | `insider` (hub colleague) | `authority` |
---
## Tell Observation Format
Tells are behavioral signals that update `EntityKnowledge.known_attributes`. When observed, two things happen: the attribute updates in the knowledge graph, and a monologue line fires.
### Structure
A tell observation monologue line should:
1. **Name the behavior** — what the player sees
2. **Interpret through character lens** — what it means to THIS character
3. **Map to an attribute** — note in `notes` which `known_attributes` key updates
```yaml
- id: terminal_m_100
text: "Kael keeps checking his lattice. Waiting for something."
character: smuggler
trigger: observe_anomaly
mood: [concerned]
topic: [colleague]
prerequisite:
relationship_state: Friendly
subject: kael_davan
notes: "First tell. Updates behavior_flags: lattice_checking. Smuggler notices but doesn't flag yet."
```
```yaml
- id: terminal_m_101
text: "Dock worker Davan — lattice activity spiked. Three pings in two minutes. Expecting a message? Or checking for surveillance?"
character: detective
trigger: observe_anomaly
mood: [analytical]
topic: [investigation]
prerequisite:
relationship_state: Known
subject: kael_davan
notes: "Same tell, different read. Updates behavior_flags: lattice_checking."
```
### Tell progression
Tells accumulate. Write them as a sequence, each gated on increasing knowledge:
**Tell 1 — first observation (no prior flags):**
```yaml
prerequisite:
relationship_state: Friendly
subject: kael_davan
# No behavior_flags gate — this IS the first observation
```
**Tell 2 — pattern forming (first flag set):**
```yaml
prerequisite:
known_attributes:
subject: kael_davan
key: behavior_flags
value_contains: lattice_checking
# Requires tell 1 to have fired and set the attribute
```
**Tell 3 — conclusion (multiple flags):**
```yaml
prerequisite:
all:
- known_attributes:
subject: kael_davan
key: behavior_flags
value_contains: lattice_checking
- known_attributes:
subject: kael_davan
key: behavior_flags
value_contains: evasive
```
Each tell should feel like a natural observation, not a checklist. The player doesn't know they're accumulating evidence.
### Canonical tell attributes
| Attribute key | What it captures | Example values |
|--------------|-----------------|----------------|
| `behavior_flags` | Comma-separated observations | `"nervous,lattice_checking"`, `"avoidance_pattern,evasive"` |
| `tell_observed` | Specific personal tell | `"looks_left_when_lying"`, `"leaves_when_torek_arrives"` |
| `contradiction_flagged` | THE FRIEND arc trigger | `"meeting_unknown_contact"`, `"avoidance_inconsistency"` |
| `secret_held` | What observer believes target hides | `"ring membership"`, `"unreported evidence"` |
| `secret_confidence` | Certainty level | `"suspected"`, `"likely"`, `"confirmed"` |
### THE FRIEND tell arcs
**Kael (smuggler observes):**
| Phase | Tell | Behavior flag | Line tone |
|-------|------|---------------|-----------|
| First crack | Increased lattice checking | `lattice_checking` | Concern: "Kael's distracted today." |
| Pattern | Shortened conversations, evasive | `distracted,evasive` | Worry: "Kael's been quiet lately." |
| Contradiction | Unauthorized spatial presence | `contradiction_flagged: meeting_unknown_contact` | Alarm: "That wasn't anyone from our rotation." |
| Confirmation | Looks left when answering | `tell_observed: looks_left_when_lying` | Hurt: "He always looks left when he's making something up." |
**Sera (detective observes):**
| Phase | Tell | Behavior flag | Line tone |
|-------|------|---------------|-----------|
| First departure | Leaves when Torek arrives (once) | `avoidance_pattern` (begins) | Barely noticed: "Sera headed out. Long day." |
| Second departure | Different excuse, same trigger | `avoidance_pattern` (accumulating) | Noted: "Sera left again. She's been tired lately." |
| Third departure | Pattern confirmed | `contradiction_flagged: avoidance_inconsistency` | Analytical: "Three times. Three excuses. That's data." |
| Deflection | Controlled redirect when asked | `tell_observed: controlled_deflection` | Conflicted: "She's not lying. But she's not telling me everything." |
---
## Voice Discipline: Writing for Dual-Lens
Every NPC line must work for both character perspectives. Same words, different meaning.
### The rule
Monologue pools are separate (D-032). But dialogue is shared — the NPC says the same thing to both characters. The `access` tier filters which lines each character can hear, but `public` and `peer` lines reach both. When an NPC speaks, the words must land differently depending on who's listening.
### How to write dual-lens lines
**Step 1: Write the line.** Concrete, in the speaker's voice. The NPC doesn't know who's listening.
```yaml
- id: hub_d_044
role: dock_worker
text: "Container 4471 sat in temp storage overnight. Scheduling backed up again."
access: [public, peer]
trust: surface
situation: [shift_start]
```
**Step 2: Check both readings.** Document the split.
```yaml
dual_lens:
smuggler: "Operational context — part of the ring pipeline. 'Backed up' is cover language."
detective: "Anomaly — overnight exceeds standard procedure. Flag for investigation."
```
**Step 3: Verify neither reading breaks.** If it only works for one character, use a different `access` tier or rely on character-specific monologue to recontextualize.
### Dual-lens patterns
**Innocuous surface / operational subtext:**
> "Voss adjusted the night shift again. Two workers short."
- Smuggler: coverage gap for tonight's run
- Detective: scheduling irregularity worth tracking
**Social warmth / threat assessment:**
> "Sera left early. Headache, she said."
- Detective: pattern forming — third time when Torek's here
- Smuggler: Commission person leaving, less oversight
**Emotional resonance / clinical data:**
> "Kael's been quiet lately. Not like him."
- Smuggler: concern for a friend
- Detective: behavioral change in a subject
**Casual disclosure / institutional intelligence:**
> "I ran the usual calibration checks today."
- Detective: routine work, mundane
- Smuggler: Commission is still active in the district
### What to avoid
- **One-character lines in shared pools.** If only the smuggler gets it, it needs `insider` access, not `public`.
- **Telegraphing.** "I'm definitely not doing anything illegal" — nobody talks like that.
- **Breaking NPC voice for the lens.** The NPC doesn't know who's listening. They talk like themselves.
- **Over-explaining.** If you need a paragraph of `dual_lens` notes to explain the split, the line isn't doing its job. The best dual-lens lines work on surface for one character and subtext for the other.
---
## Concrete Examples: Full Tagged Pools
Three pools, one per v0.1 social site. Each shows file structure, progression through THE FRIEND arc, and full tag usage.
### The Terminal (Logistics Hub) — Smuggler Monologue
```yaml
# File: content/terminal/monologue-smuggler.yaml
# Location: The Terminal (Logistics Hub)
# Character: Smuggler
lines:
# --- ARRIVAL ---
- id: terminal_m_001
character: smuggler
trigger: enter_location
prerequisite: null
text: "Morning shift. Recycled air and cargo lubricant. Home sweet home."
mood: [comfortable]
topic: [routine]
situation: [arrival, shift_start]
access: [insider]
trust: surface
tags: [baseline]
- id: terminal_m_002
character: smuggler
trigger: enter_location
prerequisite:
fact: contraband.ring_exists
confidence: KnowsDetails
text: "Shift transition in thirty minutes. If Voss kept the schedule thin, the route's clear."
mood: [analytical]
topic: [cargo]
situation: [shift_transition]
access: [insider]
trust: real
tags: [ring, operational]
# --- KAEL: THE FRIEND ARC ---
# Phase 1: Comfort
- id: terminal_m_010
character: smuggler
trigger: observe_npc
prerequisite:
relationship_state: Friendly
subject: kael_davan
text: "Kael's already at the dock. Good. The day's better when he's on shift."
mood: [fond]
topic: [colleague]
situation: [shift_start]
access: [insider, peer]
trust: surface
tags: [kael, baseline, pre_contradiction]
notes: "Phase 1 warmth. Establishes the bond before it gets tested."
# Phase 2: First crack
- id: terminal_m_011
character: smuggler
trigger: observe_anomaly
prerequisite:
all:
- relationship_state: Friendly
subject: kael_davan
- known_attributes:
subject: kael_davan
key: behavior_flags
value_contains: lattice_checking
text: "Kael keeps checking his lattice. Waiting for a message? Not like him to be jumpy."
mood: [concerned]
topic: [colleague]
situation: [routine]
access: [insider]
trust: real
tags: [kael, tell, first_crack]
notes: "Phase 2. Smuggler notices, doesn't flag it. Updates behavior_flags."
- id: terminal_m_012
character: smuggler
trigger: post_conversation
prerequisite:
known_attributes:
subject: kael_davan
key: behavior_flags
value_contains: evasive
text: "Kael cut lunch short again. 'Things to take care of.' Since when does Kael have things to take care of that don't involve me?"
mood: [worried]
topic: [colleague]
situation: [routine]
access: [insider]
trust: real
tags: [kael, tell, pattern_forming]
# Phase 3: The contradiction
- id: terminal_m_013
character: smuggler
trigger: witness_interaction
prerequisite:
relationship_state: Friendly
subject: kael_davan
text: "Kael? By the service hatch? And that's — I don't know them. Not crew, not ring. Who is that?"
mood: [concerned]
topic: [colleague]
situation: [observation]
access: [insider]
trust: real
tags: [kael, the_friend, contradiction, critical_moment]
notes: >
THE CONTRADICTION. Kael in restricted area with unknown contact.
Sets contradiction_flagged: meeting_unknown_contact.
RelationshipState: Friendly -> PersonOfInterest. Green -> amber.
# Phase 4: Confrontation aftermath
- id: terminal_m_014
character: smuggler
trigger: post_conversation
prerequisite:
all:
- relationship_state: PersonOfInterest
subject: kael_davan
- known_attributes:
subject: kael_davan
key: tell_observed
value: looks_left_when_lying
text: "He looked left. He always looks left when he's making something up. Two years I've known that tell. Kael is lying to me."
mood: [conflicted]
topic: [trust]
situation: [confrontation]
access: [insider]
trust: real
tags: [kael, the_friend, tell_confirmation]
notes: "The tell pays off. Established early in casual observation, confirmed under pressure."
# Phase 5: Contaminated trust
- id: terminal_m_015
character: smuggler
trigger: observe_npc
prerequisite:
all:
- relationship_state: PersonOfInterest
subject: kael_davan
- known_attributes:
subject: kael_davan
key: contradiction_flagged
value: meeting_unknown_contact
text: "Same old Kael. Same jokes, same routine. ...What are you not telling me?"
mood: [conflicted]
topic: [colleague]
situation: [routine]
access: [insider]
trust: real
tags: [kael, the_friend, contaminated_trust]
notes: "Phase 5. Warmth and doubt simultaneously. Every smile now has a question mark."
# --- OPERATIONAL ---
- id: terminal_m_020
character: smuggler
trigger: observe_anomaly
prerequisite:
fact: investigation.cargo_anomaly
confidence: Suspects
text: "That container's been in temp since yesterday. Someone forgot to process it, or someone's stalling."
mood: [concerned]
topic: [cargo]
situation: [routine]
access: [insider]
trust: real
tags: [investigation, cargo]
- id: terminal_m_021
character: smuggler
trigger: time_idle
prerequisite:
fact: world.shift_schedule
confidence: KnowsDetails
text: "Thirteen-forty. Transition window opens in fifty minutes. After that, twenty minutes of reduced oversight."
mood: [analytical]
topic: [routine]
situation: [routine]
access: [insider]
trust: real
tags: [ring, timing, operational]
```
---
### The Last Shift (Bar) — Detective Monologue
```yaml
# File: content/bar/monologue-detective.yaml
# Location: The Last Shift (Bar)
# Character: Detective
lines:
# --- ARRIVAL ---
- id: bar_m_001
character: detective
trigger: enter_location
prerequisite: null
text: "The Last Shift. Grain spirit and low conversation. Only place in this district that doesn't smell like freight lubricant."
mood: [comfortable]
topic: [routine]
situation: [arrival, bar_evening]
access: [public]
trust: surface
tags: [baseline, atmosphere]
- id: bar_m_002
character: detective
trigger: return_visit
prerequisite:
fact: progress.initial_suspicion
confidence: KnowsOf
text: "Back again. People talk here. And people who don't talk are talking too — just not out loud."
mood: [analytical]
topic: [investigation]
situation: [bar_evening]
access: [authority]
trust: real
tags: [investigation, atmosphere]
# --- SERA: THE FRIEND ARC ---
# Phase 1: Trust
- id: bar_m_010
character: detective
trigger: observe_npc
prerequisite:
relationship_state: Friendly
subject: sera_venn
text: "Sera's in her usual booth. Good. One familiar face in a district that doesn't want me here."
mood: [relieved]
topic: [colleague]
situation: [bar_evening]
access: [peer]
trust: surface
tags: [sera, baseline, trust]
notes: "Phase 1. Sera is the detective's anchor. Establish this warmth — it has to hurt later."
# Phase 2: Background
- id: bar_m_011
character: detective
trigger: post_conversation
prerequisite:
relationship_state: Friendly
subject: sera_venn
text: "Sera's connected here. Knows the regulars, knows the routines. And she's got diagnostic access. Useful."
mood: [comfortable]
topic: [colleague]
situation: [social]
access: [peer]
trust: surface
tags: [sera, background, pre_contradiction]
notes: "Phase 2. The detective notes Sera's access level without realizing it's the source of the conflict."
# Phase 3: Pattern — first departure
- id: bar_m_012
character: detective
trigger: witness_interaction
prerequisite:
relationship_state: Friendly
subject: sera_venn
text: "Sera headed out. Early shift tomorrow, she said. Fair enough."
mood: [comfortable]
topic: [colleague]
situation: [bar_evening]
access: [peer]
trust: surface
tags: [sera, avoidance, departure_1]
notes: "First Torek departure. Detective barely notices."
# Phase 3: Pattern — second departure
- id: bar_m_013
character: detective
trigger: witness_interaction
prerequisite:
known_attributes:
subject: sera_venn
key: behavior_flags
value_contains: avoidance_pattern
text: "Sera left again. Headache. Right after Torek walked in. ...Noted."
mood: [suspicious]
topic: [colleague]
situation: [bar_evening]
access: [peer]
trust: real
tags: [sera, avoidance, departure_2]
notes: "Second departure. Detective notes it. Not yet a pattern."
# Phase 3: Pattern — the third (contradiction)
- id: bar_m_014
character: detective
trigger: witness_interaction
prerequisite:
known_attributes:
subject: sera_venn
key: contradiction_flagged
value: avoidance_inconsistency
text: "That's the third time. Early shift, headache, kiosk check. Three excuses, one result. Sera leaves when Torek arrives. That's not coincidence."
mood: [analytical]
topic: [colleague]
situation: [bar_evening, observation]
access: [peer]
trust: real
tags: [sera, the_friend, contradiction_discovery]
notes: >
THE CONTRADICTION. Three data points make a conclusion.
Sets contradiction_flagged: avoidance_inconsistency.
RelationshipState: Friendly -> PersonOfInterest. Green -> amber.
Different from Kael's — cumulative, not sudden.
# Phase 4: The question asked
- id: bar_m_015
character: detective
trigger: post_conversation
prerequisite:
all:
- relationship_state: PersonOfInterest
subject: sera_venn
- known_attributes:
subject: sera_venn
key: tell_observed
value: controlled_deflection,topic_change
text: "She deflected. Smooth — 'he's just loud.' Technically true. But her shoulders shifted. Sera, what do you know about Torek?"
mood: [conflicted]
topic: [trust]
situation: [confrontation]
access: [authority]
trust: real
tags: [sera, the_friend, post_confrontation]
notes: "Phase 4. The detective hears the non-lie. Deflection IS the tell."
# Phase 5: Contaminated trust
- id: bar_m_016
character: detective
trigger: return_visit
prerequisite:
relationship_state: PersonOfInterest
subject: sera_venn
text: "Same booth. Same warm smile. Same offer to buy me a drink. ...Everything except whatever she's not saying."
mood: [conflicted]
topic: [trust]
situation: [bar_evening]
access: [authority]
trust: real
tags: [sera, the_friend, contaminated_trust]
notes: "Phase 5. Every kindness now has a question mark. The past is retroactively contaminated."
# --- TOREK ---
- id: bar_m_020
character: detective
trigger: observe_npc
prerequisite:
relationship_state: Unknown
subject: torek_lintar
text: "Loud one at the bar. Buying rounds he probably can't afford. Worth watching."
mood: [analytical]
topic: [money]
situation: [bar_evening]
access: [public]
trust: surface
tags: [torek, first_observation, spending]
# --- ATMOSPHERE ---
- id: bar_m_030
character: detective
trigger: time_idle
prerequisite:
fact: progress.initial_suspicion
confidence: KnowsOf
text: "Everyone's friendly enough. Drinks, jokes, the usual complaints about shift schedules. But friendly and honest aren't the same thing."
mood: [suspicious]
topic: [routine]
situation: [bar_evening]
access: [public]
trust: real
tags: [atmosphere, investigation, early_game]
```
---
### Corridor B-7 — Smuggler Monologue
```yaml
# File: content/corridor/monologue-smuggler.yaml
# Location: Corridor B-7 (Maintenance / Smuggling Route)
# Character: Smuggler
lines:
# --- ENTRY ---
- id: corridor_m_001
character: smuggler
trigger: enter_location
prerequisite:
fact: location.corridor_b7_restricted
confidence: KnowsDetails
text: "B-7. Old prefab, minimal coverage. The route works because nobody updated the surveillance map."
mood: [analytical]
topic: [routine]
situation: [night_shift]
access: [insider]
trust: real
tags: [ring, operational, spatial]
- id: corridor_m_002
character: smuggler
trigger: enter_location
prerequisite:
fact: location.corridor_b7_restricted
confidence: Suspects
text: "Maintenance corridor. Quiet. Shouldn't be this quiet during shift change."
mood: [suspicious]
topic: [routine]
situation: [shift_transition]
access: [public]
trust: surface
tags: [exploration, spatial]
notes: "Lower confidence — smuggler who doesn't know B-7's significance yet."
# --- ENVIRONMENTAL ---
- id: corridor_m_003
character: smuggler
trigger: hear_sound
prerequisite:
fact: location.corridor_b7_restricted
confidence: KnowsOf
text: "Footsteps. Wrong cadence for maintenance crew. Someone's being careful."
mood: [concerned]
topic: [danger]
situation: [night_shift]
access: [insider]
trust: real
tags: [tension, spatial_awareness]
- id: corridor_m_004
character: smuggler
trigger: time_idle
prerequisite:
fact: location.surveillance_gaps
confidence: KnowsDetails
text: "No Meridian signal down here. No eyes, no ears. Whatever happens in B-7 stays in B-7."
mood: [relieved]
topic: [routine]
situation: [alone]
access: [insider]
trust: real
tags: [ring, surveillance, operational]
# --- THE CONTRADICTION ---
- id: corridor_m_010
character: smuggler
trigger: witness_interaction
prerequisite:
relationship_state: Friendly
subject: kael_davan
text: "Kael? Down here? And that's — I don't know them. Not crew, not ring. Who the hell is that?"
mood: [concerned]
topic: [colleague]
situation: [observation]
access: [insider]
trust: real
tags: [the_friend, contradiction, kael, critical_moment]
notes: >
THE CONTRADICTION. The moment the smuggler's world shifts.
Kael in a restricted corridor with an unknown contact.
Sets contradiction_flagged: meeting_unknown_contact.
RelationshipState: Friendly -> PersonOfInterest.
Color: green -> amber. This moment is the emotional core of the arc.
- id: corridor_m_011
character: smuggler
trigger: post_conversation
prerequisite:
all:
- relationship_state: PersonOfInterest
subject: kael_davan
- known_attributes:
subject: kael_davan
key: tell_observed
value: looks_left_when_lying
text: "He looked left. Always looks left when he's making something up. Two years, Kael. I know your tells."
mood: [conflicted]
topic: [trust]
situation: [confrontation]
access: [insider]
trust: real
tags: [the_friend, kael, tell_confirmation]
notes: "Tell payoff. Established early in casual dialogue, confirmed here under pressure."
# --- EVIDENCE ---
- id: corridor_m_020
character: smuggler
trigger: discover_evidence
prerequisite:
fact: location.dead_drop_location
confidence: Direct
text: "Locker 47. Registered to someone who transferred out eighteen months ago. Convenient."
mood: [analytical]
topic: [cargo]
situation: [investigation]
access: [insider]
trust: real
tags: [dead_drop, evidence]
- id: corridor_m_021
character: smuggler
trigger: observe_anomaly
prerequisite:
fact: location.smuggling_route
confidence: KnowsDetails
text: "Fresh marks on the service hatch. Kosse's been through here. Right on schedule."
mood: [comfortable]
topic: [cargo]
situation: [night_shift]
access: [insider]
trust: real
tags: [ring, route, operational]
# --- RETURN ---
- id: corridor_m_030
character: smuggler
trigger: return_visit
prerequisite:
all:
- fact: location.corridor_b7_restricted
confidence: KnowsDetails
- relationship_state: PersonOfInterest
subject: kael_davan
text: "B-7 again. Last time I was here, Kael was talking to someone he shouldn't have been. The corridor feels different now."
mood: [suspicious]
topic: [colleague]
situation: [alone]
access: [insider]
trust: real
tags: [kael, the_friend, post_contradiction, return]
notes: "The space is contaminated by what happened here. Location memory."
```
---
## File Naming and Directory Structure
Content is organized by location and character. Location is implicit from directory — don't duplicate it in per-line tags.
```
content/
terminal/
monologue-smuggler.yaml
monologue-detective.yaml
dialogue.yaml
bar/
monologue-smuggler.yaml
monologue-detective.yaml
dialogue.yaml
corridor/
monologue-smuggler.yaml
monologue-detective.yaml
dialogue.yaml
```
Monologue files are hard-partitioned by character (D-032). One file per character per location. Dialogue files are shared — access-tier filtering happens at runtime.
### ID Convention
`{location}_{type}_{###}`
- **location**: `terminal`, `bar`, `corridor` (or `hub`, `corr` as shorthand)
- **type**: `m` (monologue), `d` (dialogue), `e` (environmental)
- **number**: Sequential per file, starting at 001
---
## Authoring Checklist
Before submitting a line:
1. **Who is speaking?** Monologue: which character? Dialogue: which role?
2. **What do they know?** Does the prerequisite reference a real FactId, RelationshipState, or attribute key from the catalogs?
3. **What triggers it?** One of 9 types. Does the trigger match the game moment?
4. **Dual-lens check.** If dialogue, does it read differently for smuggler vs detective? Document in `dual_lens`.
5. **Brevity.** Monologue competes with gameplay. If it's longer than one breath, cut it.
6. **Voice.** Does it sound like the character? The detective analyzes. The smuggler worries about people. NPCs talk like themselves, not like game designers.
7. **Show don't tell.** "The lock's been forced" not "Someone has broken into this room."
8. **Access tier.** Is the line available to the right people? Does PersonOfInterest correctly lock out trust content?
---
**Cross-References:**
- [D-028](../../../decisions/content.md#d-028-dialogue-architecture--tagged-line-pools-with-four-relational-layers): Dialogue architecture, four relational layers
- [D-032](../../../decisions/content.md#d-032-separate-monologue-pools-per-character): Separate monologue pools per character
- [D-035](../../../decisions/content.md#d-035-converged-tag-taxonomy-for-dialogue-and-monologue-line-pools): Converged tag taxonomy
- [Fact Catalog](../knowledge/fact-catalog.md): All 24 FactIds for prerequisites
- [Entity Attributes](../knowledge/entity-attributes.md): 14 canonical known_attributes keys
- [Relationship States](../knowledge/relationship-states.md): 5 states, transitions, access tier mapping
*If this guide contradicts a decision file, the decision file wins.*