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>
30 KiB
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.
# 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
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
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:
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
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
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
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:
- Every FRIEND candidate is always present in the world at full Tier 1 quality
- Only the SELECTED FRIEND gets the contradiction arc ACTIVATED
- The unselected candidates are the best Tier 2 NPCs in the district — richer than normal because they were written at Tier 1 depth
- 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 afriend_poolfield 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
# 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
# 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
- The smuggler CHOOSES. Not "caught between" — actively deciding which side to favor. Real consequence, can't have both.
- 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).
- 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.
- 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)
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:
- 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_dayandrest_daysplit - Tier 1: 7+ entries with deviations specified
- Cultural context paragraph — prose annotations for writers, referencing the regional brief
- 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 |