feat(simulation): dialogue pipeline, ContentSlug, and walk-away KG recording

Implement full dialogue selection pipeline (D-028): 4-layer filtering
engine with access tier, situation derivation, trust tier, and weighted
topic+mood scoring via SimRng. Add ContentSlug component for stable
content identity across save/load. Add walk-away KG recording with
IncompleteInteraction events per D-064 three-phase consequences. Bump
protocol to v8 with DialogueResponseEvent. Fixes #305, #427, #452.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-02-17 17:41:23 +01:00
co-authored by Claude Opus 4.6
parent 695d2ac843
commit 35f55cfa46
9 changed files with 1834 additions and 7 deletions
+37
View File
@@ -29,6 +29,25 @@ pub enum KnowledgeEventType {
},
/// Entity left observer's LOS (downgrades from Direct).
LeftLOS { target: Entity },
/// Observer walked away from an active interaction (D-064).
/// Records incompleteness in the target's known_attributes for future
/// dialogue/monologue consequences.
IncompleteInteraction {
target: Entity,
interaction_type: InteractionType,
},
}
/// Type of interaction for walk-away recording (D-064).
/// Differentiates casual conversation from confrontation —
/// future dialogue may react differently.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum InteractionType {
/// Normal Talk conversation.
Talk,
/// Confrontation (D-063). Walking away from confrontation
/// carries heavier consequences than casual talk.
Confront,
}
/// Resource: queue of pending knowledge events.
@@ -98,6 +117,24 @@ pub fn process_knowledge_events(
tracing::error!("LeftLOS target {:?} not in EntityRegistry", target);
}
}
KnowledgeEventType::IncompleteInteraction {
target,
interaction_type,
} => {
if let Some(stable_id) = registry.to_stable(target) {
observer_kg.record_incomplete_interaction(
&stable_id,
interaction_type,
event.tick,
);
tracing::debug!(
"Recorded incomplete {:?} interaction with {:?} at tick {}",
interaction_type,
stable_id,
event.tick,
);
}
}
}
}
}
+55
View File
@@ -145,6 +145,61 @@ impl KnowledgeGraph {
}
}
/// Record an incomplete interaction with an entity (D-064 walk-away).
///
/// Appends to known_attributes["incomplete_interactions"] as a
/// comma-separated list of "tick:type" entries. Creates the entity
/// entry if it doesn't exist (at Suspects confidence).
pub fn record_incomplete_interaction(
&mut self,
target: &StableId,
interaction_type: super::events::InteractionType,
tick: u64,
) {
let entry = self
.entities
.entry(*target)
.or_insert_with(|| EntityKnowledge {
last_known_position: None,
last_observed_tick: 0,
last_updated_tick: 0,
confidence: KnowledgeConfidence::Suspects,
source: KnowledgeSource::DirectObservation { tick },
state: KnowledgeState::Active,
relationship: RelationshipState::Unknown,
known_attributes: BTreeMap::new(),
});
let type_str = match interaction_type {
super::events::InteractionType::Talk => "talk",
super::events::InteractionType::Confront => "confront",
};
let record = format!("{}:{}", tick, type_str);
entry
.known_attributes
.entry("incomplete_interactions".to_string())
.and_modify(|v| {
v.push(',');
v.push_str(&record);
})
.or_insert(record);
entry.last_updated_tick = tick;
}
/// Check if the observer has any incomplete interactions with an entity.
///
/// Returns true if known_attributes["incomplete_interactions"] exists
/// and is non-empty. Used by dialogue/monologue systems to gate
/// post-conversation reactions (D-064 phase 3).
pub fn has_incomplete_interaction(&self, target: &StableId) -> bool {
self.entities
.get(target)
.and_then(|e| e.known_attributes.get("incomplete_interactions"))
.is_some_and(|v| !v.is_empty())
}
/// Set relationship state for an entity.
pub fn set_relationship(&mut self, target: &StableId, state: RelationshipState) {
if let Some(entry) = self.entities.get_mut(target) {
+1 -1
View File
@@ -12,7 +12,7 @@ pub mod graph;
pub mod registry;
pub mod types;
pub use events::{KnowledgeEvent, KnowledgeEventQueue, KnowledgeEventType};
pub use events::{InteractionType, KnowledgeEvent, KnowledgeEventQueue, KnowledgeEventType};
pub use graph::KnowledgeGraph;
pub use registry::{EntityRegistry, StableEntityId};
pub use types::*;