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
+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) {