fix(simulation): address PR #50 review — determinism, overflow, invariants

- Sort eligible NPCs by StableId for deterministic conversation pairing (D-010)
- Replace single_mut() with per-observer iteration (D-010 principle 3, D-027)
- Widen current_stress * 100 to i32 in mood derivation to prevent i16 overflow
- Promote line_interval to named constant LINE_INTERVAL_TICKS
- Add 4 Sprint 14 component invariants (MoodState, InteractionMemory,
  ActivityState+PathRequest exclusion, double-tagged tier detection)
- Attach MoodState to content spawn pipeline and gauntlet fixup
- VecDeque for InteractionMemory.notable_events (O(1) pop_front)
- Rename who_knows() to who_knows_full_scan() to communicate O(N) cost
- Add O(N^2) growth limit comment on conversation pair scan
- Document Suspicious/Focused as externally-set moods on enum variants

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-02-20 19:18:34 +01:00
co-authored by Claude Opus 4.6
parent 019a7b2221
commit f0e0673c99
7 changed files with 205 additions and 28 deletions
+42
View File
@@ -495,6 +495,48 @@ pub fn setup_gauntlet(app: &mut App) {
}
app.insert_resource(snapshots);
// --- Sprint 14 component fixup ---
// Attach MoodState and InteractionMemory to all Npc entities that are
// missing them. Gauntlet room builders don't include these yet — this
// ensures invariant S14-1/S14-2 pass and the mood/trust systems have
// valid component targets.
{
use crate::npc::interaction::InteractionMemory;
use crate::npc::mood::MoodState;
use crate::npc::Npc;
let missing_mood: Vec<bevy_ecs::prelude::Entity> = {
let mut q = app
.world_mut()
.query_filtered::<bevy_ecs::prelude::Entity, (
bevy_ecs::prelude::With<Npc>,
bevy_ecs::prelude::Without<MoodState>,
)>();
q.iter(app.world()).collect()
};
for entity in missing_mood {
app.world_mut()
.entity_mut(entity)
.insert(MoodState::default());
}
let missing_mem: Vec<bevy_ecs::prelude::Entity> = {
let mut q = app
.world_mut()
.query_filtered::<bevy_ecs::prelude::Entity, (
bevy_ecs::prelude::With<Npc>,
bevy_ecs::prelude::Without<InteractionMemory>,
)>();
q.iter(app.world()).collect()
};
for entity in missing_mem {
app.world_mut()
.entity_mut(entity)
.insert(InteractionMemory::default());
}
}
app.insert_resource(registry);
}