fix(simulation): address PR #10 review — plugin wiring, stale reset, warnings

- Register KnowledgePlugin in main.rs and game_loop test (Tyre critical)
- Add KnowledgeGraph component to player spawn (Tyre critical)
- Reset Stale -> Active on fresh direct observation (Hoshe warning)
- Make registry/queue non-optional in emit_observation_events (Tyre/Hoshe)
- Add tracing::warn for missing EntityRegistry entries (Hoshe warning)
- Replace HashSet with Vec for small entity ID lookups (Hoshe suggestion)
- Add const static assertion for KnowledgeConfidence ordering (Hoshe)
- Add is_empty() and known_facts_iter() to KnowledgeGraph (Tyre)
- Add decay_skips_non_minute_ticks and observe_resets_stale tests (Hoshe)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-02-12 01:06:33 +01:00
co-authored by Claude Opus 4.6
parent a68a3ef429
commit 3115c7a84c
6 changed files with 113 additions and 15 deletions
+55
View File
@@ -78,11 +78,15 @@ pub fn process_knowledge_events(
KnowledgeEventType::DirectObservation { target, position } => {
if let Some(stable_id) = registry.to_stable(target) {
observer_kg.observe_entity(stable_id, position, event.tick);
} else {
tracing::warn!("DirectObservation target {:?} not in EntityRegistry", target);
}
}
KnowledgeEventType::LeftLOS { target } => {
if let Some(stable_id) = registry.to_stable(target) {
observer_kg.observe_entity_leaving_los(&stable_id, event.tick);
} else {
tracing::warn!("LeftLOS target {:?} not in EntityRegistry", target);
}
}
}
@@ -230,4 +234,55 @@ mod tests {
schedule.add_systems(process_knowledge_events);
schedule.run(&mut world); // Should not panic
}
#[test]
fn decay_skips_non_minute_ticks() {
use crate::simulation::time::SimulationTime;
let mut world = World::new();
let mut registry = EntityRegistry::new(0);
let target_entity = world.spawn_empty().id();
let target_sid = registry.register(target_entity);
let mut kg = KnowledgeGraph::new();
kg.observe_entity(target_sid, TilePosition::new(5, 5, 0), 0);
kg.observe_entity_leaving_los(&target_sid, 1); // KnowsDetails
let observer = world.spawn(kg).id();
registry.register(observer);
world.insert_resource(registry);
let thresholds = DecayThresholds {
decay_after: 5,
stale_after: 100,
};
world.insert_resource(thresholds);
// Tick 7: not a multiple of 10, decay should NOT run
world.insert_resource(SimulationTime { tick: 7, paused: false });
let mut schedule = bevy_ecs::schedule::Schedule::default();
schedule.add_systems(decay_knowledge);
schedule.run(&mut world);
let kg = world.entity(observer).get::<KnowledgeGraph>().unwrap();
assert_eq!(
kg.confidence_of(&target_sid),
Some(KnowledgeConfidence::KnowsDetails),
"decay should not run on tick 7 (not a game-minute boundary)"
);
// Tick 10: multiple of 10, decay SHOULD run (age = 10 > decay_after = 5)
world.insert_resource(SimulationTime { tick: 10, paused: false });
let mut schedule2 = bevy_ecs::schedule::Schedule::default();
schedule2.add_systems(decay_knowledge);
schedule2.run(&mut world);
let kg = world.entity(observer).get::<KnowledgeGraph>().unwrap();
assert_eq!(
kg.confidence_of(&target_sid),
Some(KnowledgeConfidence::KnowsOf),
"decay should run on tick 10 and downgrade confidence"
);
}
}