# 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** |