Purpose: This is the language-agnostic specification for the game's data model. It defines what we're building, not how it's currently implemented.
What belongs here:
- Conceptual data model and relationships
- Design rationale and principles
- Planned features and future extensions
- Implementation-agnostic patterns
What does NOT belong here:
- JavaScript code or syntax
- Current implementation details (see IMPLEMENTATION.md)
- Implementation-specific workarounds
- File paths or module references
Maintenance: Keep this updated as the design evolves, even if features aren't implemented yet.
The system follows an idealist model: reality exists as beliefs in minds, not as objective truth.
Nested Perspectives:
- World mind "dreams" reality - contains possibility distributions that collapse on observation
- NPC minds model inner representations of the outer world (world mind)
- Nested recursively: NPCs can model other NPCs' minds, who model other minds, etc.
Each mind contains its own state sequence tracking what it believes over time.
Instances (beliefs in states):
- Observable entities in the world: rocks, taverns, NPCs
- Exist in specific states with ownership (
in_mindis a Mind reference) - Can be learned about via
learn_about()(copies traits from outer to inner mind) - Have
@aboutlinking inner beliefs to outer instances
Prototypes (shared beliefs):
- Inheritance templates: archetypes and cultural knowledge
- Not observable - exist only for
basesinheritance - No ownership (
in_mind = null,origin_state = null) - Cannot be learned about - only inherited from
- Tracked via
@timestampmeta-trait instead oforigin_state
Key distinction: You observe and learn about instances. You inherit from prototypes.
Cultural knowledge represents beliefs that exist in the minds of groups or archetypes, not just individual NPCs.
Structure:
- Lives in prototype minds within prototypes (e.g., Villager prototype has villager_mind)
- The prototype mind contains beliefs about world entities (tavern, workshop, etc.)
- These beliefs are not in Eidos itself - they're nested within prototype minds
- NPCs that inherit from the prototype also inherit this cultural knowledge
Example:
Eidos:
Villager prototype (belief)
├─ villager_mind (nested mind trait)
│ └─ beliefs about tavern, workshop (cultural knowledge)
│ - These beliefs have @about linking to world entities
│ - Scoped to the world hierarchy they reference
Scoping:
- Cultural beliefs reference world entities (cross-boundary references)
- Universal subjects (eternal forms) can be used by any belief
- Particular subjects (instantiated) can only be used by beliefs in their instantiating mind
- This prevents different game worlds from accidentally sharing entity identities
Usage:
- NPC instances inherit from Villager prototype → inherit villager_mind knowledge
- Cultural knowledge can be further nested (NPC's model of other NPCs' cultural beliefs)
- Enables shared worldview without duplicating beliefs for each NPC
See IMPLEMENTATION.md "Core Philosophy" for implementation details.
- Same structure works for objects, events, observations, memories, plans
- Enables beliefs about beliefs (theory of mind)
- Allows conflicting beliefs between different minds
- Observations create constraints on possibility space
- Story engine finds templates matching current constraints
- Mismatched about references create misunderstandings
- Descriptive properties enable detective-style investigation
- Flyweight pattern via base reduces memory for shared beliefs
- Minds nested in actors avoid separate enumeration
- Only instantiate beliefs when observed/relevant to story
This foundation supports:
- NPCs with different beliefs about same entities
- Testimony chains (source: "villager_1 told me")
- Possibility distributions (location: [60% shed, 30% workshop, 10% unknown])
- Investigation templates (Event_missing_item with victim/offender slots)
- Temporal reasoning (beliefs changing over time)
- Sequential access mimics brain/LLM processing patterns
- Event logs for working memory, consolidated summaries for long-term storage
- Attention filters determine which events get detailed encoding
- Temporal indexing separates "current state" from "historical chain" queries
- Sleep cycles for memory consolidation during natural game breaks
- Compress observational chains when intermediate steps lack story relevance
- Differential storage - only record what changed between observations
- LOD-based aging - detailed observations decay to general familiarity over time
- Immutable records with append-only changes
- Lazy materialization of inheritance chains rather than pre-computation
- Periodic belief compaction to collapse unnecessary version chains
- Cleanup phases that consolidate chains not serving active story threads
- Separate semantic reasoning from storage optimization
- Database handles indexing/queries, reasoning engine operates on cached subsets
- Redis for change subscriptions and fast lookups
- Materialized views for flyweight inheritance patterns
- Procedural generation seeded by neighboring LOD properties
- Details removed when player leaves (tracks, movable objects that could reasonably change)
- Hierarchical simulation with different timesteps per abstraction level
- Village belief nodes as search boundaries
- Vector embeddings for semantic similarity matching
- Spatial partitioning for observation relevance
- Property-based indexing for constraint satisfaction queries
The core insight: optimize for working sets during story generation rather than full graph traversal, with periodic consolidation maintaining long-term consistency.
- Everything is beliefs in minds (world_mind, npc minds, player mind)
- Objects are immutable - changes create new versions with
baseinheritance - States use prototype chain lookup (like JavaScript)
- Time is represented by simple
tickliterals - Certainty uses discrete levels: certain, common, unusual, rare
- States track
added,removed,updatedrather than full belief lists - Multiple states can exist at same tick (superposition)
- States inherit via
basefrom any previous state - Each mind has its own state sequence
- States track forward links via
branchesfor navigation - Nested mind states can ground to parent mind states via
ground_state
States can track another timeline via the tracks property:
trackspoints to a specific state in the tracked timeline (at the same vt)- Overlay semantics: Local beliefs win by subject; unhandled subjects fall through to tracks
- States with
tracksmust havebase: null(no shared ancestry with tracked timeline) - Used for: alt timelines, theories tracking core observations, committed branches
Example:
alt_state_6:
base: null # NO shared base with core
tracks: core_state_6 # tracked timeline state at same vt
_insert: [belief_bob_forest] # local deviation- New versions inherit via
base(e.g., hammer_2 base: hammer_1) - Conflicts tracked via
conflicts_with: [observations] - Objects can split when partially observed
- Cleanup phases can merge objects back together
- Immutability: Objects never change, only new versions created
- Lazy evaluation: States only track changes, not full object lists
- Cleanup phases: Can merge similar objects when differences no longer matter
- Save points: Each state branch is essentially a saveable game state
- No ground truth: Everything exists as possibilities until observed/constrained
This structure supports:
- Temporal reasoning (via ticks and state inheritance)
- Multiple perspectives (each mind has own states)
- Uncertainty/superposition (multiple states at same tick)
- Constraint propagation (via conflicts_with)
- Efficient memory use (via base inheritance)
- Everything is beliefs in minds - All game entities (objects, NPCs, locations) are represented as Beliefs
- Global registry with ownership - Beliefs stored in global registry, each associated with the mind that instantiated it. Subjects are either universals (eternal forms) or particulars (instantiated in specific mind)
- Hierarchical access - Parent minds can access child minds (theory of mind) through nested belief structures
- Prototype inheritance - Beliefs inherit from Archetypes and other Beliefs via
bases - Immutable nodes - Changes create new versions via
baseinheritance - Branching on uncertainty - Multiple states can exist at the same tick
- Time progression - States are indexed by tick number
- Differential updates - States track
insert/removerather than full belief lists - Label uniqueness - Labels are globally unique across beliefs and archetypes
- Shared cultural knowledge - Beliefs in template/cultural minds that other minds reference via
basesinheritance - Identity-based references - Traits store subject IDs (integers) not object references, enabling temporal queries and state branching at scale
To support temporal queries, state branching, and scaling to billions of objects, the system uses identity-based references rather than direct object references.
Every belief has two identifiers:
- sid (subject ID) - Stable identity that persists across all versions of a belief about the same subject
- id (version ID) - Unique identifier for each specific temporal version of a belief
Both are integers from a single global sequence:
id_sequence = 0
// Create new subject
hammer.sid = ++id_sequence // 1
hammer.id = ++id_sequence // 2
// Create new version of same subject
hammer_v2.sid = 1 // Reused - same subject
hammer_v2.id = ++id_sequence // 3 - new unique version
Key properties:
- Every
idis globally unique and never reused sidvalues are reused across versions of the same subject- Same sequence eliminates lookup ambiguity
- Version chains share the same
sidbut have differentidvalues
Trait values store subject IDs as integers, not object references:
hammer:
sid: 100
id: 101
traits:
location: 50 # Integer sid, not object reference
color: "red" # Primitive values stored directlyTo use a reference, you must resolve it within a state context:
// Stored value is just an integer
hammer.traits.location = 50
// Resolution finds the appropriate version
state.resolve_subject(50) → workshop_v3 # Latest version visible in this state
Benefits of ID-based references:
- Temporal queries - Same
sidcan resolve to different versions at different times - State branching - Same
sidresolves differently in different branches - No stale references - References never break when targets update
- Trivial serialization - Traits are just integers, no recursive object tracking
- Scales to billions - No cascade updates when objects change
- DB-agnostic - Resolution can be optimized with indexing, SQL, partitioning, etc.
The about field links beliefs across mind boundaries using subject IDs:
# World mind (ground truth)
world.hammer:
sid: 100
id: 101
about: null # Root subject
# NPC mind (belief about world's hammer)
npc.hammer_belief:
sid: 200 # NPC's local subject ID
id: 201
about: 100 # Links to world's hammer via sid (integer, not reference)Semantic meaning:
sid = 100identifies the "actual" hammer in world mindsid = 200identifies the NPC's concept of the hammerabout = 100means "my concept is about that actual thing"
Each mind maintains its own subject space, with about creating traceable links from belief → ground truth.
The system uses three distinct time dimensions to support temporal reasoning, nested minds, and knowledge provenance:
When a state or belief was created in computational time.
Properties:
- Always set and never goes backwards within a state chain
- Used to query "what states existed at this point in the simulation"
- Child mind states synchronize with parent via fork invariant:
child.tt = parent_state.vt
Example uses:
- Finding the current state of a mind at simulation tick 100
- Querying which version of a belief was active when an event occurred
- Coordinating nested mind timelines with their parent contexts
What time the state is thinking/reasoning about.
Properties:
- Defaults to transaction time (present thinking)
- Can differ from tt to enable temporal reasoning:
vt < tt- Remembering the pastvt = tt- Reasoning about the presentvt > tt- Planning for the future
- Same tt + same ground_state = superposition (different possibilities at same moment)
- Same tt + different ground_state = different versions in parent's timeline
Example uses:
- NPC recalling what the workshop looked like yesterday (vt=50, tt=100)
- NPC planning a future action (vt=110, tt=100)
- Multiple conflicting beliefs about the current state (superposition)
When information was learned or decided upon.
Properties:
- Lives at the trait level (provenance metadata)
- Enables testimony chains and knowledge tracking
- Distinguishes direct observation from hearsay
Example uses:
- "I saw the hammer at tick 50" (dt=50, direct observation)
- "Guard told me hammer was stolen at tick 40" (dt=60, hearsay about tick 40)
- Resolving conflicting information by recency or source reliability
Ground state coordination:
- Ground state owns the canonical valid time
- Child minds synchronize:
child.tt = ground_state.vt(fork invariant) - No arithmetic on time values - always coordinate via explicit ground state reference
Temporal consistency:
- Transaction time must progress forward:
next_tt >= current_tt - Valid time can move freely (past, present, future)
- Decision time tracks when knowledge was acquired (trait-level metadata)
See IMPLEMENTATION.md for how these concepts map to the current JavaScript implementation.
Note: The following examples show beliefs grouped under minds for readability, but in the actual design, beliefs are stored in a global registry with in_mind references.
world_mind:
beliefs:
hammer_1:
archetype: PortableObject
descriptors: [black, heavy]
location: workshop_1
npc1:
archetype: NPC
location: workshop_1
mind: npc1_mind
npc2:
archetype: NPC
location: market_1
mind: npc2_mind
npc1_mind:
states:
state_1:
tick: 1
added: [] # Empty mind initially
beliefs: {}
npc2_mind:
states:
state_1:
tick: 1
added: []
beliefs: {}Note: Beliefs are shown grouped under minds for readability, but are actually stored in global belief_by_id and belief_by_sid registries with in_mind references.
world_mind:
states:
state_2:
base: state_1
tick: 2
updated: [hammer_1_v2]
added: [event_take_hammer]
beliefs:
hammer_1_v2:
archetype: PortableObject
base: hammer_1
location: npc1_inventory
event_take_hammer:
archetype: Event_movement
actor: npc1
target: hammer_1
from: workshop_1
to: npc1_inventory
time: tick_2npc1_mind:
states:
state_3a: # Branch: NPC2 saw me
base: state_2
tick: 3
added: [npc1_model_npc2_v1a]
certainty: unusual
state_3b: # Branch: NPC2 didn't see
base: state_2
tick: 3
added: [npc1_model_npc2_v1b]
certainty: common
beliefs:
npc1_model_npc2_v1a:
archetype: mind
about: npc2_mind
states:
state_3:
tick: 3
ground_state: npc1_mind.state_3a # Grounded in NPC1's branch where they assume they were seen
added: [obs_saw_take, npc2_belief_event, npc2_belief_hammer]
beliefs:
obs_saw_take:
archetype: Event_perception
observer: npc2
target: npc2_belief_event
time: tick_2
about: world_perception_event # world's version of this observation
npc2_belief_event:
archetype: Event_movement
about: event_take_hammer # corresponds to world event
actor: npc1
target: npc2_belief_hammer
source: obs_saw_take
npc2_belief_hammer:
archetype: PortableObject
about: hammer_1 # corresponds to world's hammer
npc1_model_npc2_v1b:
archetype: mind
about: npc2_mind
states:
state_3:
tick: 3
ground_state: npc1_mind.state_3b # Grounded in NPC1's branch where they assume they weren't seen
added: [] # No observation
beliefs: {}Building on branch 3a (assumes NPC2 saw):
npc1_mind:
states:
state_4:
base: state_3a
tick: 4
added: [facade_for_player]
beliefs:
facade_for_player:
archetype: mind
states:
state_4:
tick: 4
added: [false_hammer_location]
beliefs:
false_hammer_location:
archetype: PortableObject
descriptors: [black, heavy]
location: workshop_1 # The lie - claims it's still there
about: hammer_1 # corresponds to world's hammerNPC1 thinks NPC2 might be unsure about what they saw:
npc1_mind:
states:
state_5:
base: state_4
tick: 5
updated: [npc1_model_npc2_v2]
beliefs:
npc1_model_npc2_v2:
archetype: mind
base: npc1_model_npc2_v1a
about: npc2_mind
states:
state_5a: # NPC2 thinks they saw NPC1
tick: 5
added: [obs_saw_take]
certainty: unusual
state_5b: # NPC2 thinks they saw someone else
tick: 5
added: [obs_saw_someone, npc2_belief_event_v2]
certainty: common
beliefs:
obs_saw_someone:
archetype: Event_perception
observer: npc2
target: npc2_belief_event_v2
time: tick_2
npc2_belief_event_v2:
archetype: Event_movement
base: npc2_belief_event
about: event_take_hammer
actor: unknown_person # Uncertain whoProperties flow through bases references:
# Linear version chain
hammer_1 → hammer_1_v2 → hammer_1_v3
↓
location: workshop → npc1_inventory → npc2_inventory
# Branching version chain
country_culture_v1 (autumn)
↓ branches
├─→ country_v2 (winter, @T110)
└─→ country_v3 (spring, @T150)
The about property links beliefs across mind boundaries:
world.hammer_1 ← player.hammer_belief ← player.obs_hammer (source)
States track forward navigation via branches:
state_2:
branches: [state_3a, state_3b] # Multiple possible futures
state_3a:
base: state_2
certainty: unusual
branches: [state_4]
state_3b:
base: state_2
certainty: common
branches: [] # No further exploration yetUse cases:
- Uncertainty: Multiple possible current states (superposition)
- Planning: NPC considers alternative future actions
- Theory of mind: Reasoning about what others might believe
- Cleanup: Merge branches when differences no longer matter
Nested mind states link back to outer mind via ground_state:
# NPC's mind state grounded in world state
npc_mind.state_5:
ground_state: world_mind.state_10 # Created when world was at tick 10
# NPC's model of another NPC grounded in their own state
npc1_model_of_npc2.state_3:
ground_state: npc1_mind.state_7 # NPC1's reasoning at tick 7Properties:
- Tracks which version of outer reality a nested mind is reasoning about
- Passed explicitly when creating new states via
branch_state(ground_state, vt) - Root states (world_mind) have
ground_state: null - Enables temporal coordination between nested minds
When beliefs contradict:
belief_x:
base: belief_original
conflicts_with: [observation_y]world_mindhas references tonpc1_mind,npc2_mind(stored as beliefs with Mind trait)npc1_mindhas references tonpc1_model_npc2,facade_for_player(nested mind beliefs)- Each mind is isolated from siblings and parent, but can introspect children
- All beliefs (including those "belonging to" a mind) exist in the global registry with
in_mindreferences
Beliefs can have child versions (branches) representing alternative states:
country_culture_v1 (autumn)
↓ branches: [v2, v3]
├─→ country_culture_v2 (winter, @T110)
└─→ country_culture_v3 (spring, @T150)
Query resolution selects appropriate child based on context:
- Query @T90 → v1 (autumn) - no children yet
- Query @T110 → v2 (winter) - temporal match
- Query @T150 → v3 (spring) - temporal match
Inherited beliefs see updates without creating new versions:
npc_belief (bases: [city_belief → country_v1])
Query @T110:
→ country_v1.branches → v2
→ Result: winter trait from v2
No npc_belief_v2 created until explicitly needed
Promotion types:
- Temporal: Different time periods (seasons, news events) - resolves to concrete value
- Probability: Multiple possible states with certainty (king alive 60%, dead 40%) - resolves to Fuzzy
- Spatial: Regional variations in knowledge (future)
- Perspective: Different beliefs held by different minds (future)
Goal: Enable efficient updates to shared cultural beliefs without cascading version creation across millions of NPCs. Beliefs lazily resolve inherited traits through promotion detection and state-based resolution, only materializing new versions when explicitly created.
The problem: When shared cultural knowledge updates (e.g., "it's winter now"), thousands of NPCs inherit this through city → country belief chains. Without lazy propagation, updating country requires creating new versions for every city and every NPC - millions of belief objects.
The solution: Promotions. When a shared belief updates, we create only one new version (the promotion) and register it on the original. All inheriting beliefs automatically see the promoted version at query time - no cascade of new versions needed.
Without promotions:
country_v1 → country_v2
→ Must create city_v2 for each city (100 versions)
→ Must create npc_v2 for each NPC (millions of versions)
With promotions:
country_v1.promotions = [country_v2]
→ Cities/NPCs query traits → resolver walks to country_v2
→ Total: 1 new belief, not millions
Terminology: A "promotion" is a belief that propagates to all children of its base. The term reflects that the new version is "promoted" to visibility for the entire subtree of inheritors. This is distinct from regular versioning where each node needs its own new version.
Key insight: Only the updated node has promotions. City and NPC beliefs remain unchanged - they inherit from a node that has promotions. Trait resolution walks the chain and uses the state's resolver to pick the appropriate promotion.
When to use promotions:
- Shared beliefs in Eidos that evolve over time (seasons, news, cultural events)
- Any belief where many other beliefs inherit from it
- Created via
replace(state, traits, {promote: true})
Creating promotions:
# Original shared belief
country_culture_v1:
traits: {season: 'autumn'}
promotions: []
# Create promotion via replace(..., {promote: true})
country_culture_v2:
bases: [country_culture_v1]
origin_state: state_110 # When this promotion was created
certainty: null # null = temporal (not probability)
traits: {season: 'winter'}
# v1 now tracks the promotion
country_culture_v1:
promotions: [country_culture_v2]Promotion properties:
origin_state: State where promotion was created (used for temporal filtering)certainty: null for temporal promotions, 0-1 for probability promotionsconstraints: Future extensibility for spatial/contextual constraints
Most promotions are temporal - they represent knowledge updates at specific times. The resolver picks the appropriate version based on query timestamp:
# Query at T100 (before promotion): returns 'autumn'
# Query at T110 (after promotion): returns 'winter'
npc_knowledge:
bases: [city_paris → country_culture_v1]
# Query @T110:
# Walk: npc → city → country_v1
# Detect: country_v1.promotions = [v2]
# Resolve: v2.origin_state.tt=110 <= query.tt=110 ✓
# Result: season = 'winter' from v2 (concrete value)Key point: Temporal promotions resolve to concrete values, not Fuzzy.
When multiple promotions exist at the same timestamp with different certainty values, they represent genuine uncertainty and are joined into Fuzzy trait values:
# Two probability promotions at same timestamp
king_dead:
certainty: 0.6
traits: {status: 'dead'}
king_alive:
certainty: 0.4
traits: {status: 'alive'}
# When materializing, joined into single belief:
king_joined:
bases: [king_original]
traits:
status: Fuzzy {
alternatives: [
{value: 'dead', certainty: 0.6},
{value: 'alive', certainty: 0.4}
]
}Key behavior:
- Multiple promotions are joined, not selected
- Each trait becomes a Fuzzy with alternatives weighted by certainty
- Uncertainty is preserved in trait values rather than collapsed
- Aligned with "no objective truth" philosophy
Promotions enable efficient updates without cascading version creation:
# Tick 100: Initial state
country_culture_v1:
traits: {season: 'autumn'}
promotions: []
city_paris:
bases: [country_culture_v1]
npc_knowledge:
bases: [city_paris]
# Query: npc → city → country_v1 → season = 'autumn'# Tick 110: Country updates (1 new belief via promotion)
country_culture_v1:
promotions: [country_culture_v2]
country_culture_v2:
bases: [country_culture_v1]
origin_state: state_110
traits: {season: 'winter'}
# City and NPC beliefs UNCHANGED
city_paris:
bases: [country_culture_v1] # Still points to v1
npc_knowledge:
bases: [city_paris] # Still points to city
# Query @T110:
# npc → city → country_v1
# Detect: country_v1.promotions = [v2]
# Resolve: v2 created @T110, query @T110
# Result: season = 'winter' from v2
#
# No new city or NPC beliefs created!Materialization on demand:
When explicitly creating a new version, the promotion chain is materialized:
# First NPC creates explicit version @T110
npc1_knowledge_v2 = npc1_knowledge_v1.replace(state, {opinion: 'I hate winter'}, {promote: true})
# Materialization process:
# Walk: npc1_v1 → city → country_v1 (has promotions)
# Resolve: country_v2 selected @T110
# Create: city_paris_v2 {bases: [country_v2]}
# Result: npc1_v2 {bases: [city_v2]}
# Second NPC reuses materialized city:
npc2_knowledge_v2:
bases: [city_paris_v2] # Reuses city_v2
# Result: 1000 NPCs share 1 city_v2Benefits:
- Shared knowledge updates: O(1) new beliefs
- Query resolution: O(inheritance depth)
- Materialization: Only when explicitly creating versions
- Memory: O(changes) not O(NPCs)
country_mind:
state_100:
insert: [country_culture_v1]
beliefs:
country_culture_v1:
subject_id: 100
bases: [Culture]
traits: {season: 'autumn'}
branches: []
city_paris_mind:
state_100:
insert: [city_paris_v1]
beliefs:
city_paris_v1:
subject_id: 200
bases: [country_culture_v1]
traits: {city_name: 'Paris'}
npc1_mind:
state_100:
insert: [npc1_knowledge_v1]
beliefs:
npc1_knowledge_v1:
subject_id: 300
bases: [city_paris_v1]
traits: {}Query: What season does NPC1 know?
npc1_knowledge_v1
→ bases: city_paris_v1
→ bases: country_culture_v1
→ traits.season = 'autumn'
country_mind:
state_110:
base: state_100
insert: [country_culture_v2]
beliefs:
country_culture_v1:
branches: [country_culture_v2] # Child version added
country_culture_v2:
subject_id: 100 # Same subject as v1
bases: [country_culture_v1]
origin_state: state_110
traits: {season: 'winter'}
branches: []
# City and NPC beliefs unchanged
city_paris_v1:
bases: [country_culture_v1] # Still points to v1
npc1_knowledge_v1:
bases: [city_paris_v1] # Still points to city_v1Query @T110: What season does NPC1 know?
npc1_knowledge_v1
→ bases: city_paris_v1
→ bases: country_culture_v1
→ branches: [country_v2]
→ Resolve @T110: v2 (created @T110)
→ traits.season = 'winter'
Result: NPC1 knows 'winter' without creating npc1_v2 or city_v2
npc1_mind:
state_115:
base: state_100
insert: [npc1_knowledge_v2]
beliefs:
npc1_knowledge_v2:
subject_id: 300 # Same subject
bases: [city_paris_v2] # Now points to materialized city_v2
origin_state: state_115
traits: {opinion: 'I hate winter'}
# Materialization created city_v2:
city_paris_v2:
subject_id: 200
bases: [city_paris_v1, country_culture_v2]
origin_state: state_115
traits: {city_name: 'Paris'} # Inherited from v1Materialization process:
- Create npc1_knowledge_v2 with new trait
- Walk bases: npc1_v1 → city_v1 → country_v1
- Detect country_v1.branches = [country_v2]
- Resolve @T115: select country_v2
- Create city_paris_v2 inheriting from country_v2
- Update npc1_knowledge_v2 to inherit from city_v2
npc2_mind:
state_120:
insert: [npc2_knowledge_v2]
beliefs:
npc2_knowledge_v2:
bases: [city_paris_v2] # Reuses existing city_v2!
traits: {opinion: 'I love winter'}No new city version created - city_paris_v2 already materialized by NPC1.
Scaling: 1000 NPCs forming opinions → 1 country_v2 + 1 city_v2 + 1000 npc versions
Templates query across branches to find story opportunities:
- Deception possible when
facade_for_playerdiffers fromstate_actual - Investigation needed when multiple branches have different certainties
- Confrontation available when conflicting observations exist