feat(simulation): wire NPC pool generation and authored triangle instantiation (#176, #188)

Production startup now spawns 23 Sova NPCs with EntanglementTag
(Flat/Intrigue) based on triangle_membership. Three-phase spawn:
entity creation, cross-reference resolution, and authored triangle
instantiation. Five triangles (3 ActiveFork, 2 PassiveTension per
D-087) with deterministic IDs via FNV-1a hashing.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-02-28 16:34:36 +01:00
co-authored by Claude Opus 4.6
parent daae3dd6ab
commit 42febf4059
8 changed files with 381 additions and 10 deletions
+10 -2
View File
@@ -15,8 +15,8 @@ use bevy_ecs::{schedule::Schedule, world::World};
use settled_reach_server::{
content::template::{
apply_resolve_triangle, tick_triangle_escalation, ResolveTriangleCommand,
ResolveTriangleQueue, TemplateId, TriangleCrisisEventQueue, TriangleDef, TriangleId,
TrianglePhase, TriangleState,
ResolveTriangleQueue, TemplateId, TriangleClassification, TriangleCrisisEventQueue,
TriangleDef, TriangleId, TrianglePhase, TriangleState,
},
knowledge::{registry::EntityRegistry, types::StableId, StableEntityId},
npc::ToleranceThreshold,
@@ -68,6 +68,7 @@ fn spawn_triangle(
phase,
tension_rate,
template_id: TemplateId(1),
classification: TriangleClassification::default(),
},
))
.id()
@@ -329,6 +330,7 @@ fn d026_non_active_tier_triangle_not_escalated() {
phase: TrianglePhase::Simmering,
tension_rate: 5,
template_id: TemplateId(1),
classification: TriangleClassification::default(),
})
.id();
@@ -395,6 +397,7 @@ fn resolve_command_sets_phase_to_resolved() {
phase: TrianglePhase::Active,
tension_rate: 3,
template_id: TemplateId(1),
classification: TriangleClassification::default(),
})
.id();
@@ -423,6 +426,7 @@ fn d089_resolve_does_not_cascade() {
phase: TrianglePhase::Active,
tension_rate: 3,
template_id: TemplateId(1),
classification: TriangleClassification::default(),
})
.id();
@@ -434,6 +438,7 @@ fn d089_resolve_does_not_cascade() {
phase: TrianglePhase::Simmering,
tension_rate: 2,
template_id: TemplateId(1),
classification: TriangleClassification::default(),
})
.id();
@@ -445,6 +450,7 @@ fn d089_resolve_does_not_cascade() {
phase: TrianglePhase::Active,
tension_rate: 4,
template_id: TemplateId(1),
classification: TriangleClassification::default(),
})
.id();
@@ -485,6 +491,7 @@ fn resolve_twice_is_idempotent() {
phase: TrianglePhase::Active,
tension_rate: 1,
template_id: TemplateId(1),
classification: TriangleClassification::default(),
})
.id();
@@ -602,6 +609,7 @@ fn d087_all_v01_conflict_types_produce_escalatable_states() {
phase: TrianglePhase::Simmering,
tension_rate: 3,
template_id: TemplateId(1),
classification: TriangleClassification::default(),
};
assert_eq!(
state.phase,