Long-term content strategy workshop: 300-world generator model, cultural ingredients menu, three-system NPC architecture (9 patterns x 6 motivations), Sacred/Profane/Middle Kingdom framework. 9 agents across 4 rounds plus lead interview establishing the production path from hand-authored Sova to generated 300 worlds. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
635 lines
30 KiB
Markdown
635 lines
30 KiB
Markdown
# Round 2 — Gestalt (Systems Design)
|
|
|
|
*cracks knuckles*
|
|
|
|
Five tensions to reconcile. Let me work through each one.
|
|
|
|
---
|
|
|
|
## 1. Content Directory Convergence (Gestalt + Tyre)
|
|
|
|
I read Tyre's proposal carefully. We agree on ~80% of the structure. Let me map where we align, where we differ, and what the converged structure should be.
|
|
|
|
### Where We Agree
|
|
|
|
| Area | Consensus |
|
|
|------|-----------|
|
|
| Global/district split | Global content (factions, tech, contraband, knowledge) separate from per-district content |
|
|
| Districts as atomic pack | `districts/{district-slug}/` is the content unit |
|
|
| Per-district NPCs | `districts/{slug}/npcs/{npc}.yaml` |
|
|
| Per-district locations | `districts/{slug}/locations/{location}.yaml` |
|
|
| Schema files | Validation schemas at the top level |
|
|
| Mod overlay | Additive by default, explicit override declarations |
|
|
|
|
### Where We Differ (and Resolution)
|
|
|
|
| My Proposal | Tyre's Proposal | Resolution | Why |
|
|
|---|---|---|---|
|
|
| `_global/` | `global/` | **Tyre wins: `global/`** | Underscore prefix is for infrastructure (`_schema/`, `_meta/`), not game content. Factions are game content. |
|
|
| Separate `dialogue/` and `monologue/` dirs | `lines/{location}/` with files inside | **Tyre wins: `lines/`** | Cleaner. `lines/terminal/dialogue.yaml` + `lines/terminal/monologue-smuggler.yaml` keeps everything per-location without parallel directory trees. |
|
|
| `triangles/` as standalone dir | `templates/` (social site defs that include triangle specs) | **Tyre wins: `templates/`** | Triangles are a property of social sites, not standalone objects. A triangle definition makes no sense without its template context. |
|
|
| `facts/` per district for local FactId instances | All facts in `global/knowledge/facts.yaml` | **I push back here.** See below. |
|
|
| `regions/` for style guides | Not addressed | **Add `global/regions/`** | Miri's Krenn brief needs a home. Regional style guides are global (same culture applies wherever that culture exists). |
|
|
| No manifest | `_meta/manifest.yaml` | **Tyre wins: add `_meta/`** | Needed for pack metadata, load order, versioning. |
|
|
|
|
### The Local Facts Question
|
|
|
|
Tyre puts all 24 FactIds in `global/knowledge/facts.yaml`. I disagree for one specific category: **spatial/location facts**.
|
|
|
|
`contraband.ring_exists` is a legitimate global definition — the CONCEPT of a smuggling ring is vocabulary. But `location.corridor_b7_restricted` is meaningless outside Sova Transit District. If we add a second district, it will have its OWN restricted corridors with different names. These are per-district facts.
|
|
|
|
**Proposed split:**
|
|
|
|
```
|
|
global/knowledge/
|
|
facts.yaml # Abstract/universal FactIds: contraband.*, world.*, relationship.*
|
|
entity-attributes.yaml # 14 canonical keys
|
|
relationship-states.yaml
|
|
|
|
districts/sova-transit/
|
|
facts.yaml # District-specific FactIds: location.*, investigation.*, progress.*
|
|
```
|
|
|
|
The engine merges both at load time. Content authors know: "Am I defining a concept (global) or a location-specific observation (district)?"
|
|
|
|
This also solves Nigel's randomizer requirement — per-seed variation can swap which district facts exist without touching global vocabulary.
|
|
|
|
### Converged Structure
|
|
|
|
```
|
|
content/
|
|
_meta/
|
|
manifest.yaml
|
|
load-order.yaml
|
|
|
|
_schema/
|
|
npc.schema.yaml
|
|
location.schema.yaml
|
|
faction.schema.yaml
|
|
template.schema.yaml
|
|
fact.schema.yaml
|
|
dialogue.schema.yaml
|
|
monologue.schema.yaml
|
|
district.schema.yaml
|
|
|
|
global/
|
|
factions/
|
|
concord-assembly.yaml
|
|
lattice-commission.yaml
|
|
syndics.yaml
|
|
guardians-of-autonomy.yaml
|
|
veil-institute.yaml
|
|
the-unbound.yaml
|
|
technology/
|
|
neural-lattice.yaml
|
|
meridian.yaml
|
|
span-gates.yaml
|
|
founder-gates.yaml
|
|
clone-transfer.yaml
|
|
severance-tech.yaml
|
|
contraband/
|
|
lattice-components.yaml
|
|
medical-grade-replacements.yaml
|
|
severance-equipment.yaml
|
|
knowledge/
|
|
facts.yaml # Global/abstract FactIds only
|
|
entity-attributes.yaml
|
|
relationship-states.yaml
|
|
regions/
|
|
krenn-system.yaml # Miri's regional style guide
|
|
|
|
districts/
|
|
sova-transit/
|
|
district.yaml # Metadata, location list, faction presence, ambient config
|
|
facts.yaml # District-specific FactIds + starting confidences
|
|
npcs/
|
|
kael-davan.yaml
|
|
sera-venn.yaml
|
|
voss.yaml
|
|
...
|
|
locations/
|
|
the-terminal.yaml
|
|
the-last-shift.yaml
|
|
maintenance-corridors.yaml
|
|
templates/
|
|
logistics-hub.yaml # Social site: role slots, triangles, NPC assignments
|
|
bar.yaml
|
|
smuggling-ring.yaml
|
|
lines/
|
|
terminal/
|
|
dialogue.yaml
|
|
monologue-smuggler.yaml
|
|
monologue-detective.yaml
|
|
bar/
|
|
dialogue.yaml
|
|
monologue-smuggler.yaml
|
|
monologue-detective.yaml
|
|
corridor/
|
|
dialogue.yaml
|
|
monologue-smuggler.yaml
|
|
monologue-detective.yaml
|
|
```
|
|
|
|
### Mod Overlay Mechanics (Accepting Tyre's)
|
|
|
|
Tyre's three overlay operations (ADD / REPLACE / MERGE) are correct and better specified than mine:
|
|
- **Entity definitions REPLACE** (whole file — no partial NPC patches)
|
|
- **Line pools MERGE** (append, deduplicate by `id`, last-writer-wins on collision)
|
|
- **New files ADD** (no conflict)
|
|
|
|
**One addition to Tyre's spec:** I want to address his open question about mod pool flooding. If a mod adds 50 dialogue lines to a 20-line pool:
|
|
|
|
**Proposal: Weight tagging.** Each content pack declares a `weight` in its manifest (default: 1.0). The line selection system uses weight as a probability multiplier. Base content at weight 1.0, a mod at weight 0.3 means mod lines are 30% as likely to be selected as base lines. This lets modders add volume without dominating the pool.
|
|
|
|
```yaml
|
|
# In mod manifest
|
|
id: extra-bartender
|
|
weight: 0.5 # Mod lines selected at half the rate of base lines
|
|
```
|
|
|
|
### Tyre's Other Open Question: District vs Template as Atomic Pack
|
|
|
|
Tyre asked: "Is `district` the right atomic content pack unit? Or should it be `template` (social site)?"
|
|
|
|
**Answer: District.** Here's why:
|
|
|
|
Templates share NPCs (bridge NPCs span social sites). If a modder adds a new NPC to the bar, that NPC might reference Hub NPCs by name, or participate in a cross-template triangle. Packing at the template level would mean the mod has to patch multiple template packs to add one NPC — that's worse for modders.
|
|
|
|
District is the natural unit because triangles and bridge connections are district-internal. A mod that adds "a new bar NPC" is really adding "a new NPC to Sova Transit District who happens to frequent the bar." The mod touches `districts/sova-transit/` and everything resolves within that directory.
|
|
|
|
---
|
|
|
|
## 2. PC-as-NPC Converged Specification (Gestalt + Paula)
|
|
|
|
Paula and I are saying the same things at different granularity. She organized by deliverable type (required/optional/PC-only), I organized by data structure (transfer/transform/generate). Let me merge.
|
|
|
|
### The Full PC-Candidate NPC Spec
|
|
|
|
An NPC that can be upgraded to a PC must have ALL standard Tier 2+ fields PLUS a `## PC Upgrade` appendix containing structured data.
|
|
|
|
#### Fields from Standard NPC Brief (Required for ALL Tier 2+)
|
|
|
|
These 12 fields transfer directly into the PC brief (Paula's list, confirmed):
|
|
|
|
| # | Field | Source Axis | Transfer Type |
|
|
|---|---|---|---|
|
|
| 1 | Name | Core Identity | Direct |
|
|
| 2 | Age | Core Identity | Direct |
|
|
| 3 | Role | Core Identity | Direct → starting occupation + access |
|
|
| 4 | System Origin | Core Identity | Direct → cultural context |
|
|
| 5 | Lattice Tier | Core Identity | Direct → perception mode caps (D-017) |
|
|
| 6 | Employer | Core Identity | Direct → faction tag + cover |
|
|
| 7 | Want (Primary) | Axis 1 | Transform → orientation monologue seed |
|
|
| 8 | Want (Secondary) | Axis 1 | Transform → emotional anchor |
|
|
| 9 | Relationships (all) | Axis 3 | Transform → starting EntityKnowledge + RelationshipState |
|
|
| 10 | Daily Routine | Axis 5 | Transform → default schedule (deviations = player agency) |
|
|
| 11 | Information Inventory | Axis 6 | Transform → starting FactId confidences + knowledge graph |
|
|
| 12 | Faction | Inferred | Direct → access tier baseline |
|
|
|
|
#### Fields That Transform at Upgrade (Derivable or Design-Choice)
|
|
|
|
| # | Field | Status | Resolution |
|
|
|---|---|---|---|
|
|
| 13 | Secret/Vulnerability | **Partially required** | The PC knows their own secret. For smuggler: ring membership IS the secret (known to player, hidden from world). For detective: personal motive for requesting posting. The NPC brief provides the secret; the upgrade inverts it from "hidden from observer" to "player's burden to manage." |
|
|
| 14 | Contentment | Generated | From Want + Relationships + archetype. Smuggler starts moderate-low. Detective starts moderate. |
|
|
| 15 | Personality Traits | **Preset per archetype for v0.1** | Paula recommended this, I agree. Player-chosen personality is a v0.2+ feature. |
|
|
| 16 | Tell System | **Inverted** | This is the critical transform. The PC's tells become NPC-observable. Gore's insight applies: tells are how the world reads the PC. "You always check your lattice before lying" — the NPC knows. The PC doesn't control this mechanically; it fires when the PC takes certain actions. |
|
|
| 17 | Skill Set | Derived | From role + archetype. |
|
|
| 18 | Combat Tag | Derived | From archetype. |
|
|
|
|
#### PC Upgrade Appendix (New Fields, Required for PC-Candidate NPCs)
|
|
|
|
This is the section that must be added to any NPC who might become a PC. It's where Paula's 6 PC-only fields and my structured formats converge:
|
|
|
|
**Field 1: Starting EntityKnowledge Table**
|
|
|
|
```yaml
|
|
pc_upgrade:
|
|
starting_knowledge:
|
|
entities:
|
|
- target: kael_davan
|
|
state: Friendly
|
|
confidence: KnowsDetails
|
|
attributes:
|
|
name: "Kael Davan"
|
|
role: "dock worker"
|
|
faction: "ring member" # smuggler knows this
|
|
trust_level: "trusted"
|
|
relationship_type: "close colleague"
|
|
routine_pattern: "morning shift 06:00-14:00, bar after shift"
|
|
behavior_flags: "reliable,punctual"
|
|
- target: voss
|
|
state: Known
|
|
confidence: KnowsOf
|
|
attributes:
|
|
name: "Voss"
|
|
role: "shift supervisor"
|
|
faction: "civilian" # smuggler thinks this
|
|
trust_level: "reliable"
|
|
relationship_type: "authority figure"
|
|
# ... all known NPCs
|
|
```
|
|
|
|
**Field 2: Starting FactId Inventory**
|
|
|
|
```yaml
|
|
starting_facts:
|
|
- id: contraband.ring_exists
|
|
confidence: KnowsDetails
|
|
- id: contraband.lattice_components
|
|
confidence: KnowsDetails
|
|
- id: location.corridor_b7_restricted
|
|
confidence: KnowsDetails
|
|
- id: world.shift_schedule
|
|
confidence: KnowsDetails
|
|
- id: relationship.ring_membership
|
|
confidence: KnowsDetails
|
|
- id: investigation.manifest_discrepancy
|
|
confidence: null # smuggler doesn't know about this
|
|
- id: contraband.severance_tech
|
|
confidence: null # smuggler doesn't know about this
|
|
```
|
|
|
|
**Field 3: Reverse Knowledge (Bidirectional)**
|
|
|
|
This is my key contribution. Every NPC who knows the PC needs their EntityKnowledge entry defined:
|
|
|
|
```yaml
|
|
known_by:
|
|
- observer: kael_davan
|
|
state: Friendly
|
|
attributes:
|
|
name: "{PC Name}"
|
|
role: "dock worker"
|
|
faction: "ring member"
|
|
trust_level: "trusted"
|
|
relationship_type: "close colleague"
|
|
- observer: voss
|
|
state: Known
|
|
attributes:
|
|
name: "{PC Name}"
|
|
role: "dock worker"
|
|
faction: "civilian"
|
|
trust_level: "reliable"
|
|
- observer: hael
|
|
state: Known
|
|
attributes:
|
|
name: "{PC Name}"
|
|
role: "dock worker"
|
|
faction: "civilian"
|
|
trust_level: "uncertain" # knows PC, doesn't know ring
|
|
- observer: lera_sessik
|
|
state: Known
|
|
attributes:
|
|
name: "{PC first name}"
|
|
role: "bar regular"
|
|
trust_level: "reliable"
|
|
```
|
|
|
|
**Why this matters:** Without reverse knowledge, NPCs don't know the player exists at game start. Kael can't say "There you are. Good — I was starting to worry" unless Kael's knowledge graph already contains an entry for the PC with `state: Friendly`. Ozzie's "walking into Cheers" moment depends entirely on these reverse entries being populated.
|
|
|
|
**Field 4: Access Tier Map**
|
|
|
|
```yaml
|
|
access_tier_map:
|
|
kael_davan: insider
|
|
voss: peer
|
|
lera_sessik: peer
|
|
hael: peer
|
|
sera_venn: public # smuggler doesn't know Sera well
|
|
maret_korr: insider # hub colleague
|
|
drin: insider # hub colleague
|
|
torek_lintar: peer # bar regular
|
|
```
|
|
|
|
**Field 5: Orientation Monologue Hook**
|
|
|
|
```yaml
|
|
orientation:
|
|
monologue_pool: "smuggler" # which pool to activate
|
|
starting_want_text: >
|
|
Protect the operation and the people in it.
|
|
The money's good and Kael makes it manageable.
|
|
But Nils keeps pushing and Hael keeps worrying.
|
|
first_monologue_triggers:
|
|
- trigger: enter_location
|
|
location: terminal
|
|
text_ref: terminal_m_001 # "Morning shift. Recycled air and cargo lubricant."
|
|
```
|
|
|
|
**Field 6: Agency Boundaries**
|
|
|
|
```yaml
|
|
agency:
|
|
can_access: [terminal, bar, corridor_b7, residential]
|
|
restricted_without_justification: [commission_kiosk] # smuggler has no Commission business
|
|
default_cover: "dock worker on shift"
|
|
cover_breaks_when: "present in restricted areas during non-shift hours, or interacting with Commission systems"
|
|
```
|
|
|
|
### Validation Requirement
|
|
|
|
Paula's Step 5 is critical: **PC's starting knowledge must not contradict any NPC's information inventory.** If the PC "knows" Kael is in the ring, and Kael's Information Inventory says "nobody outside the ring knows" — that's consistent (PC IS ring). But if the PC "knows" Sera has unreported evidence, and Sera's profile says "nobody knows" — that's a knowledge graph violation.
|
|
|
|
The schema validator (Tyre's pipeline) should check this:
|
|
```
|
|
For each (target, attribute) in pc_upgrade.starting_knowledge.entities:
|
|
assert target NPC exists in district roster
|
|
assert attribute values are consistent with target NPC's Information Inventory
|
|
|
|
For each (observer, attribute) in pc_upgrade.known_by:
|
|
assert observer NPC exists in district roster
|
|
assert observer's Information Inventory is compatible with their knowing these attributes about PC
|
|
```
|
|
|
|
---
|
|
|
|
## 3. FRIEND Pool: Size and Cost
|
|
|
|
Nigel wants the FRIEND drawn from a pool. Gore agrees but insists THE FRIEND is never procedurally generated — the content is authored, the selection is randomized. I agree with both.
|
|
|
|
### Pool Size Analysis
|
|
|
|
| Pool Size (per character) | Total Tier 1 NPCs | Additional Authored Lines | Replayability Multiplier | v0.1 Feasible? |
|
|
|---|---|---|---|---|
|
|
| 1 (current: Kael, Sera) | 2 | 0 | 1x (no variation) | Yes (done) |
|
|
| 2 per character | 4 | ~140-200 | 4x (2 smuggler x 2 detective) | Stretch |
|
|
| 3 per character | 6 | ~280-400 | 9x | v0.2 target |
|
|
|
|
### The Graceful Degradation Insight
|
|
|
|
Here's what makes the pool work without exploding content costs: **FRIEND candidates that aren't selected as FRIEND still exist as excellent Tier 2 NPCs.**
|
|
|
|
If Kael is selected as the smuggler's FRIEND, Renn (the other candidate) is just a courier — a well-written one with a rich profile, but his contradiction arc isn't activated by the storyteller. His Phase 1 warmth exists. His tells exist. His secret exists. But the storyteller doesn't trigger the "observe contradiction in restricted area" event for Renn.
|
|
|
|
This means:
|
|
1. Every FRIEND candidate is always present in the world at full Tier 1 quality
|
|
2. Only the SELECTED FRIEND gets the contradiction arc ACTIVATED
|
|
3. The unselected candidates are the best Tier 2 NPCs in the district — richer than normal because they were written at Tier 1 depth
|
|
4. On replay, the player may encounter an NPC they KNOW has a secret (from a previous playthrough where that NPC was FRIEND) but whose arc isn't active. Gore's dramatic irony!
|
|
|
|
### Authoring Cost Per Additional FRIEND Candidate
|
|
|
|
Let me be precise about what "one additional FRIEND candidate" costs:
|
|
|
|
| Deliverable | Lines/Pages | Time Estimate |
|
|
|---|---|---|
|
|
| Full Tier 1 profile (10-axis, all sections) | ~2 pages | 3-4 hours |
|
|
| 70-100 authored dialogue/monologue lines | 70-100 lines | 8-12 hours |
|
|
| Contradiction arc (5 phases) | Part of profile | Included above |
|
|
| Tell progression (3-4 stages) | Part of profile + lines | Included above |
|
|
| Dual-lens notes for BOTH characters | ~0.5 pages | 1-2 hours |
|
|
| PC Upgrade appendix | ~0.5 pages YAML | 1-2 hours |
|
|
| Reverse knowledge entries from ALL NPCs | ~0.25 pages YAML | 1 hour |
|
|
| Cross-character authored relationship stubs (FRIEND↔PC) | 5-10 lines | 1-2 hours |
|
|
| **Total per candidate** | **~3 pages + 70-100 lines** | **~15-22 hours** |
|
|
|
|
### v0.1 Recommendation
|
|
|
|
**Ship with 1 FRIEND per character (Kael, Sera).** But architect the pool system from day one:
|
|
- The `templates/` YAML includes a `friend_pool` field listing eligible NPC IDs
|
|
- The seed selects from this pool at game start
|
|
- v0.1 pool has 1 entry per character — functionally identical to current hardcoded behavior
|
|
- v0.2 adds second candidates without any architectural change
|
|
|
|
```yaml
|
|
# In templates/logistics-hub.yaml (or a district-level config)
|
|
friend_pools:
|
|
smuggler:
|
|
- npc: kael_davan
|
|
contradiction_module: kael_exit_attempt
|
|
# v0.2: add renn or other candidate here
|
|
detective:
|
|
- npc: sera_venn
|
|
contradiction_module: sera_concealment
|
|
# v0.2: add second candidate here
|
|
```
|
|
|
|
### Gore's Constraint: Authored Relationship Depth
|
|
|
|
Gore correctly insists the PC-FRIEND relationship needs authored warmth, not just `state: Friendly`. Each FRIEND candidate needs specific relationship stubs for EVERY possible PC upgrade candidate:
|
|
|
|
- If the pool has 2 smuggler FRIENDs (Kael, Renn) and the PC could be any of 2-3 smuggler archetypes, we need: Kael↔PCa, Kael↔PCb, Renn↔PCa, Renn↔PCb relationship stubs. Each stub is 5-10 authored lines of shared history.
|
|
|
|
This is a combinatorial cost. At pool size 2x2: 4 stubs (20-40 lines). At 3x3: 9 stubs (45-90 lines). Manageable but worth tracking.
|
|
|
|
---
|
|
|
|
## 4. Triangle 1 Fix: The Nils-vs-Voss Escalation Decision
|
|
|
|
Paula and I both flagged Hub Power (T1) as the weakest triangle. The core problem: Nils is off-stage, so the smuggler never mediates a real-time confrontation. The triangle describes tension but doesn't produce a FORK.
|
|
|
|
### The Decision Moment: Volume Escalation
|
|
|
|
**Setup (storyteller-triggered, mid-game):**
|
|
|
|
Nils sends word through Renn that the next shipment is double volume. A connected system has a supply window — miss it and the opportunity is gone for months. Voss refuses to create the necessary schedule gap: "Two workers short on the same shift twice in a row? Maret will flag it. I won't do it."
|
|
|
|
Both sides appeal to the smuggler.
|
|
|
|
### The Fork
|
|
|
|
| Option | Action | Immediate Effect | Cascade to T2 | Cascade to T5 | Detective Observability |
|
|
|---|---|---|---|---|---|
|
|
| **A: Side with Nils** | Tell Voss to make it work, or work around Voss | Voss creates gap resentfully, does it clumsily | Maret notices the schedule anomaly more easily → T2 heats up | Pell sees higher volume, more risk → Pell's wavering accelerates | Schedule anomaly is louder signal. Detective monologue: "Third schedule adjustment this cycle. Pattern." |
|
|
| **B: Side with Voss** | Tell Renn to relay: "Not this cycle" | Nils is displeased, questions smuggler's loyalty | Voss is relieved, cooperates more → T2 stays cool | Ring internal tension rises from above → different pressure path on Pell | Schedule looks clean. Detective has less to work with. Smuggler's ring standing drops. |
|
|
| **C: Split the route** | Propose half-volume through normal route, half through untested alternative | Partial success. New route = new exposure surface | Maret sees half the anomaly (borderline noticeable) | Ring members stretched thinner → more behavioral tells across the board | Activity in unexpected area. Detective monologue: "Cargo movement in Section C? That's not on any manifest." |
|
|
|
|
### Mechanical Expression
|
|
|
|
```yaml
|
|
# In templates/logistics-hub.yaml or a storyteller module
|
|
decision_node:
|
|
id: volume_escalation
|
|
trigger: storyteller_mid_game # fires between minute 12-18
|
|
setup:
|
|
message_from: nils_davan # off-stage, delivered via renn
|
|
message_text: "Double volume. Next cycle. Make it happen."
|
|
voss_response: "I won't short the schedule again. Maret's watching."
|
|
options:
|
|
- id: side_with_nils
|
|
label: "Tell Voss to make it work"
|
|
effects:
|
|
- npc: voss
|
|
attribute_change: { trust_level: "resentful", exposure_risk: "escalating" }
|
|
- npc: maret_korr
|
|
behavior_flag_add: "noticed_schedule_gap"
|
|
- triangle: worried_knowledge
|
|
tension_increase: 2
|
|
- triangle: informant_question
|
|
tension_increase: 1
|
|
- investigation_signal: "schedule_anomaly_visible"
|
|
- id: side_with_voss
|
|
label: "Push back: not this cycle"
|
|
effects:
|
|
- npc: voss
|
|
attribute_change: { trust_level: "relieved", cooperation: "improved" }
|
|
- player:
|
|
attribute_change: { loyalty_assessment_by_nils: "questioned" }
|
|
- triangle: worried_knowledge
|
|
tension_increase: 0
|
|
- triangle: informant_question
|
|
tension_increase: 1 # pressure comes from above instead
|
|
- investigation_signal: null # clean schedule, less detective evidence
|
|
- id: split_route
|
|
label: "Split: half normal, half new route"
|
|
effects:
|
|
- npc: voss
|
|
attribute_change: { trust_level: "uncertain" }
|
|
- location: section_c
|
|
flag: "unexpected_cargo_activity"
|
|
- triangle: worried_knowledge
|
|
tension_increase: 1
|
|
- triangle: informant_question
|
|
tension_increase: 1
|
|
- investigation_signal: "new_route_observable"
|
|
```
|
|
|
|
### Why This Fixes T1
|
|
|
|
1. **The smuggler CHOOSES.** Not "caught between" — actively deciding which side to favor. Real consequence, can't have both.
|
|
2. **Cascading consequences.** Each choice ripples into T2 and T5. The detective's experience changes based on which option the smuggler picked (in NPC mode, the AI picks based on the NPC's personality/loyalty balance).
|
|
3. **Nils is present-by-proxy.** The message arrives through Renn. Nils doesn't need to be physically present — the DEMAND is present. The smuggler's response defines their relationship with an off-stage power.
|
|
4. **Detective observability differs per choice.** Option A is loudest (easiest for detective). Option B is quietest. Option C creates a new thread. This means the smuggler's choice shapes the detective's investigation difficulty on replay.
|
|
|
|
### Additional Fix: Nils Partial Presence
|
|
|
|
I still recommend one brief Nils comm interaction in v0.1 — even a text-only lattice message that the smuggler receives and must respond to. This costs ~5-8 authored lines and makes the off-stage vertex feel real. The response choices could mirror or foreshadow the volume escalation decision.
|
|
|
|
---
|
|
|
|
## 5. Routine Format + Cultural Patterns
|
|
|
|
My Round 1 proposed a structured `routine:` YAML block. Miri's Krenn brief introduces cultural patterns that the routine should accommodate: shift-end bar culture, kuum (transition drink), kolm (card game), 5+2 work cycles.
|
|
|
|
### The Problem
|
|
|
|
The engine needs parseable schedule data (time, location, activity). Cultural flavor is for writers, not parsers. If I embed `cultural_note` fields in the YAML, I'm mixing concerns — the schedule system ignores them, and writers have to read through engine data to find authoring guidance.
|
|
|
|
### Solution: Two Layers
|
|
|
|
**Layer 1: Engine-readable routine (YAML)**
|
|
|
|
```yaml
|
|
routine:
|
|
cycle: "5+2" # work days / rest days
|
|
schedule:
|
|
work_day:
|
|
- time: "06:00-06:30"
|
|
location: terminal
|
|
activity: shift_startup
|
|
- time: "06:30-10:30"
|
|
location: terminal
|
|
activity: work
|
|
- time: "10:30-11:00"
|
|
location: terminal_breakroom
|
|
activity: break
|
|
- time: "11:00-14:00"
|
|
location: terminal
|
|
activity: work
|
|
- time: "14:00-14:30"
|
|
location: terminal
|
|
activity: shift_end
|
|
- time: "14:30-16:00"
|
|
location: bar
|
|
activity: social
|
|
- time: "16:00-22:00"
|
|
location: residential
|
|
activity: home
|
|
rest_day:
|
|
- time: "09:00-12:00"
|
|
location: residential
|
|
activity: home
|
|
- time: "12:00-14:00"
|
|
location: bar
|
|
activity: social
|
|
- time: "14:00-22:00"
|
|
location: residential
|
|
activity: home
|
|
deviations:
|
|
- trigger: ring_operation
|
|
replaces: "22:00+"
|
|
location: corridor_b7
|
|
frequency: "1-2x per week"
|
|
- trigger: volume_escalation # the T1 decision node
|
|
replaces: "14:00-14:30"
|
|
location: corridor_b7
|
|
frequency: "once (event-driven)"
|
|
```
|
|
|
|
**Layer 2: Cultural context (prose, in the wiki NPC profile)**
|
|
|
|
Under the Daily Routine section, AFTER the YAML block:
|
|
|
|
> **Cultural context (Krenn System):**
|
|
> - Kael's shift startup involves grabbing leib from the canteen and eating standing — Krenn workers don't sit for breakfast
|
|
> - Break at 10:30 is social: lunch with the smuggler (if applicable), shared kalaa and supp
|
|
> - "Shift-end" at the bar means ordering kuum first (the spiced transition drink), then switching to grain spirit if staying. Kael usually shifts to the bar 2-3 days per work cycle
|
|
> - Hael expects Kael home by 17:00 on non-bar days. Late arrivals trigger Hael's worry axis
|
|
> - Ring operations happen during night shift transitions (22:00-23:00 window) — Kael uses "overtime" as cover to Hael
|
|
|
|
This gives writers everything they need for dialogue and monologue ("Kael orders kuum" → Mellanie knows what that is from Miri's brief) while the engine reads only the clean YAML schedule.
|
|
|
|
### How This Works With Miri's Specific Cultural Patterns
|
|
|
|
| Miri's Pattern | Where It Lives | Who Uses It |
|
|
|---|---|---|
|
|
| 5+2 work cycle | `routine.cycle: "5+2"` in YAML | Schedule system |
|
|
| Shift-end bar migration | `schedule.work_day` entry with `location: bar` after shift | Schedule system + monologue triggers |
|
|
| Kuum as transition drink | Cultural context prose + Mellanie's menu + dialogue tags | Writers (not engine) |
|
|
| Kolm card game timing | `schedule.rest_day` entry at bar, OR deviation with `trigger: kolm_night` | Schedule system + event system |
|
|
| Leib/kalaa food | Cultural context prose | Writers (environmental text, dialogue flavor) |
|
|
| Greeting conventions (no verbal greeting, chin-lift) | NOT in routine — in Miri's regional brief | Writers (dialogue `notes` field) |
|
|
|
|
### Style Guide Mandate
|
|
|
|
Every NPC routine section in the wiki should contain:
|
|
|
|
1. **YAML block** — engine-readable, schema-validated, per-tier minimum entries:
|
|
- Tier 3: 3+ time-location entries (minimum: work, social, home)
|
|
- Tier 2: 5+ entries with `work_day` and `rest_day` split
|
|
- Tier 1: 7+ entries with deviations specified
|
|
2. **Cultural context paragraph** — prose annotations for writers, referencing the regional brief
|
|
3. **Deviation list** — what pulls the NPC off their routine, with triggers and frequencies
|
|
|
|
The YAML block is the mechanical requirement. The cultural context is the authoring aid. Both are mandatory; neither replaces the other.
|
|
|
|
---
|
|
|
|
## Cross-Cutting: Paula's Smuggler Attribute Vocabulary
|
|
|
|
Paula proposed 5 smuggler-specific entity attribute keys. This is a strong proposal that I want to endorse and refine, because it directly addresses the "smuggler content pipeline is under-served" problem she identified.
|
|
|
|
### Endorsed Keys (with mechanical integration)
|
|
|
|
| Key | Values | What Drives It | What It Gates |
|
|
|---|---|---|---|
|
|
| `operational_reliability` | `reliable`, `slipping`, `compromised`, `unknown` | NPC behavior + ring event outcomes | Smuggler monologue tone, ring assignment willingness |
|
|
| `exposure_risk` | `low`, `escalating`, `critical` | Tells + detective proximity + NPC behavior | Smuggler tension monologue, ring operational decisions |
|
|
| `loyalty_assessment` | `solid`, `uncertain`, `wavering`, `turning` | Observation of NPC behavior over time | Smuggler dialogue options (confront vs. reassure), ring strategy |
|
|
| `leverage_held` | Freeform string (e.g., `"gambling debt"`, `"family secret"`) | Discovery through observation or conversation | Confrontation/pressure dialogue unlocks |
|
|
| `social_debt` | `they_owe_me`, `i_owe_them`, `mutual`, `none` | Relationship history (authored per NPC) | Favor-asking dialogue, cooperation likelihood |
|
|
|
|
These give the smuggler's knowledge model parity with the detective's investigation attributes. The detective has `tell_observed`, `contradiction_flagged`, `secret_held`, `secret_confidence`. The smuggler now has `operational_reliability`, `exposure_risk`, `loyalty_assessment`, `leverage_held`, `social_debt`.
|
|
|
|
Both characters build profiles of NPCs. The detective's profile is investigative. The smuggler's profile is operational/social. Different lenses on the same people — which is exactly the game's thesis.
|
|
|
|
**Implementation note for Tyre:** These 5 keys go into the entity-attributes schema alongside the existing 14. The `known_attributes` BTreeMap already supports arbitrary keys — no code change needed, just schema expansion and content authoring.
|
|
|
|
---
|
|
|
|
## Summary: Decisions for Recording
|
|
|
|
| # | Decision | Type | Status |
|
|
|---|---|---|---|
|
|
| 1 | Converged content directory structure (Gestalt+Tyre) | Architecture | **Propose** |
|
|
| 2 | District-specific facts split from global facts | Architecture | **Propose** |
|
|
| 3 | Mod overlay: ADD/REPLACE/MERGE + weight tagging | Architecture | **Propose** |
|
|
| 4 | District (not template) as atomic content pack | Architecture | **Propose** |
|
|
| 5 | PC-candidate NPC spec: 12 required + 6 optional + 6 PC-only fields | Content spec | **Propose** |
|
|
| 6 | Reverse knowledge (bidirectional) as mandatory PC upgrade field | Content spec | **Propose** |
|
|
| 7 | FRIEND pool architecture: v0.1 = 1 per character, system supports N | Content pipeline | **Propose** |
|
|
| 8 | Volume Escalation decision node for Triangle 1 | Content design | **Propose** |
|
|
| 9 | Two-layer routine format: YAML (engine) + prose (writers) | Style guide | **Propose** |
|
|
| 10 | 5 smuggler-specific entity attribute keys (Paula's proposal, endorsed) | Knowledge model | **Propose** |
|