Sprint 37 dead-code sweep closing out two stale supersession chains: #877 (D-167, 2026-03-24): Removes HeritageRoot type alias and ZonePaletteModifier::Heritage variant from server/src/simulation/ generator.rs. The 7 abstract heritage roots were retired in favour of the corridor cultural system; these two stubs were the only remaining references. #878 (D-032 + cascade rule): Strips the entire CharacterArchetype (Smuggler/Detective) trace from the server. Per lead direction 2026-04-21 and the development cascade (CLAUDE.md), character/NPC/ verb-differentiation/monologue code is Phase 6 detail that should not exist in code yet. The running archetype trace was pre-cascade filler, not production — production is only the client's character- creation UI and insert screens (client follow-up in #882). Deleted: - CharacterArchetype enum + StartupMessage.character_archetype field - archetype_verb_label() + archetype branch of apply_phase2_verb_filter (D-057 character-verb differentiation — marked superseded) - MonologueState.character partitioning - Gauntlet archetype plumbing (setup_gauntlet no longer takes an archetype) - server/content/schemas/drama_module.schema.yaml (zero Rust consumers) - server/content/modules/tier1/smuggling_ring_v0_1.yaml - server/tests/archetype_monologue.rs (regression guard for the removed system) - server/tests/v01_integration_playthrough.rs (archetype-dependent) Decision updates: - decisions/content.md D-032 supersession rewritten to cite the cascade (v0.2 drop invalidated the prior D-117 framing). - decisions/content.md D-035 tag taxonomy: `character` enum footnote updated; field noted as unused, do not reintroduce without a confirmed Phase 6 design. - decisions/perception.md D-057: archetype-verb differentiation marked superseded. Also bundles the types.rs version-field removal from #874 since the file was already touched here. Full trace audit in docs/architecture/sprint-37-878-audit.md. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -14,7 +14,6 @@
|
||||
use bevy_ecs::prelude::*;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use crate::bridge::types::CharacterArchetype;
|
||||
use crate::knowledge::events::{KnowledgeEvent, KnowledgeEventQueue, KnowledgeEventType};
|
||||
use crate::knowledge::EntityRegistry;
|
||||
use crate::npc::mood::{MoodState, NpcMood};
|
||||
@@ -37,11 +36,12 @@ pub struct ExamineRequest {
|
||||
pub target: Entity,
|
||||
}
|
||||
|
||||
/// Character-filtered examination result for snapshot delivery.
|
||||
/// Examination result for snapshot delivery.
|
||||
///
|
||||
/// Content differs per CharacterArchetype:
|
||||
/// Smuggler — physical threat read, cargo-handling posture, opportunity windows.
|
||||
/// Detective — procedural tells, behavioral inconsistencies, stress indicators.
|
||||
/// Phase 6 note: per-archetype text variants (smuggler/detective flavor) were
|
||||
/// removed during the cascade cleanup. Text is a single unified "subject read"
|
||||
/// until archetype differentiation is reintroduced per the culture-driven
|
||||
/// voice system (D-121) in a later cascade phase.
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct ExamineResultEvent {
|
||||
/// Character-filtered observation text for client display.
|
||||
@@ -106,12 +106,15 @@ fn has_trait(traits_opt: Option<&PersonalityTraits>, t: PersonalityTrait) -> boo
|
||||
traits_opt.map(|p| p.traits.contains(&t)).unwrap_or(false)
|
||||
}
|
||||
|
||||
/// Generate character-filtered examination text from NPC component state.
|
||||
/// Generate examination text from NPC component state.
|
||||
/// All logic is pure, deterministic, and integer-based (D-010).
|
||||
///
|
||||
/// Archetype-specific text variants were removed during the cascade cleanup
|
||||
/// (D-032 / Sprint 37). Reintroduce per-culture voice when the cascade reaches
|
||||
/// the Phase 6 character/NPC layer (see D-121).
|
||||
pub fn generate_examine_text(
|
||||
mood: NpcMood,
|
||||
ratio: u8,
|
||||
archetype: CharacterArchetype,
|
||||
traits_opt: Option<&PersonalityTraits>,
|
||||
) -> String {
|
||||
let stress_label = match ratio {
|
||||
@@ -123,53 +126,29 @@ pub fn generate_examine_text(
|
||||
|
||||
let mood_label = mood_word(mood);
|
||||
|
||||
match archetype {
|
||||
CharacterArchetype::Smuggler => {
|
||||
// Physical threat read + cargo opportunity window
|
||||
let threat = if matches!(mood, NpcMood::Hostile | NpcMood::Suspicious) {
|
||||
"Threat posture. Don't push it."
|
||||
} else if has_trait(traits_opt, PersonalityTrait::Bold) {
|
||||
"Confident bearing. Will push back if cornered."
|
||||
} else if has_trait(traits_opt, PersonalityTrait::Cautious) {
|
||||
"Nervous type. Predictable under pressure."
|
||||
} else {
|
||||
"No obvious threat read."
|
||||
};
|
||||
let tell = if has_trait(traits_opt, PersonalityTrait::Deceptive) {
|
||||
"Controlled affect — practiced concealment."
|
||||
} else if matches!(mood, NpcMood::Anxious | NpcMood::Frustrated) {
|
||||
"Involuntary stress markers present."
|
||||
} else if matches!(mood, NpcMood::Suspicious) {
|
||||
"Scanning. Aware of being observed."
|
||||
} else if matches!(mood, NpcMood::Hostile) {
|
||||
"Threat posture. Aware of being observed."
|
||||
} else {
|
||||
"Baseline presentation."
|
||||
};
|
||||
|
||||
let window = if ratio > 60 {
|
||||
"Too distracted to track cargo movement."
|
||||
} else if matches!(mood, NpcMood::Focused) {
|
||||
"Paying close attention to this section."
|
||||
} else {
|
||||
"Standard patrol pattern. Window is there."
|
||||
};
|
||||
let read = if ratio > 60 {
|
||||
"Under pressure — potential liability or asset."
|
||||
} else if matches!(mood, NpcMood::Content | NpcMood::Warm) {
|
||||
"Comfortable. Less guarded than usual."
|
||||
} else if matches!(mood, NpcMood::Focused) {
|
||||
"Paying close attention."
|
||||
} else {
|
||||
"Routine behavior pattern."
|
||||
};
|
||||
|
||||
format!("Appears {mood_label}, {stress_label}. {threat} {window}")
|
||||
}
|
||||
|
||||
CharacterArchetype::Detective => {
|
||||
// Procedural tells + behavioral read
|
||||
let tell = if has_trait(traits_opt, PersonalityTrait::Deceptive) {
|
||||
"Controlled affect — practiced concealment."
|
||||
} else if matches!(mood, NpcMood::Anxious | NpcMood::Frustrated) {
|
||||
"Involuntary stress markers present."
|
||||
} else if matches!(mood, NpcMood::Suspicious) {
|
||||
"Scanning. Aware of being observed."
|
||||
} else {
|
||||
"Baseline presentation."
|
||||
};
|
||||
|
||||
let read = if ratio > 60 {
|
||||
"Under pressure — potential liability or asset."
|
||||
} else if matches!(mood, NpcMood::Content | NpcMood::Warm) {
|
||||
"Comfortable. Less guarded than usual."
|
||||
} else {
|
||||
"Routine behavior pattern."
|
||||
};
|
||||
|
||||
format!("Subject: {mood_label}, {stress_label}. {tell} {read}")
|
||||
}
|
||||
}
|
||||
format!("Subject: {mood_label}, {stress_label}. {tell} {read}")
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
@@ -196,7 +175,6 @@ pub fn process_examine_interaction(
|
||||
Entity,
|
||||
&TilePosition,
|
||||
&ExamineRequest,
|
||||
Option<&CharacterArchetype>,
|
||||
&mut ExamineResultBuffer,
|
||||
),
|
||||
With<PlayerCharacter>,
|
||||
@@ -212,14 +190,13 @@ pub fn process_examine_interaction(
|
||||
>,
|
||||
examine_text_query: Query<(&TilePosition, Option<&ExamineText>)>,
|
||||
) {
|
||||
let Ok((player_entity, player_pos, examine_req, archetype_opt, mut result_buffer)) =
|
||||
let Ok((player_entity, player_pos, examine_req, mut result_buffer)) =
|
||||
player_query.single_mut()
|
||||
else {
|
||||
return;
|
||||
};
|
||||
|
||||
let target = examine_req.target;
|
||||
let archetype = archetype_opt.copied().unwrap_or_default();
|
||||
|
||||
// Try NPC examine path first
|
||||
if let Ok((target_pos, mood_opt, tolerance_opt, traits_opt)) = npc_query.get(target) {
|
||||
@@ -238,7 +215,7 @@ pub fn process_examine_interaction(
|
||||
|
||||
let mood = mood_opt.map(|m| m.mood).unwrap_or(NpcMood::Neutral);
|
||||
let ratio = tolerance_opt.map(stress_ratio).unwrap_or(0);
|
||||
let text = generate_examine_text(mood, ratio, archetype, traits_opt);
|
||||
let text = generate_examine_text(mood, ratio, traits_opt);
|
||||
|
||||
kg_events.push(KnowledgeEvent {
|
||||
observer: player_entity,
|
||||
@@ -323,8 +300,8 @@ mod tests {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn smuggler_hostile_npc_gives_threat_read() {
|
||||
let text = generate_examine_text(NpcMood::Hostile, 20, CharacterArchetype::Smuggler, None);
|
||||
fn hostile_npc_gives_threat_read() {
|
||||
let text = generate_examine_text(NpcMood::Hostile, 20, None);
|
||||
assert!(
|
||||
text.contains("Threat posture"),
|
||||
"expected threat read, got: {text}"
|
||||
@@ -332,32 +309,27 @@ mod tests {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn smuggler_focused_npc_notes_attention() {
|
||||
let text = generate_examine_text(NpcMood::Focused, 30, CharacterArchetype::Smuggler, None);
|
||||
fn focused_npc_notes_attention() {
|
||||
let text = generate_examine_text(NpcMood::Focused, 30, None);
|
||||
assert!(
|
||||
text.contains("close attention"),
|
||||
text.contains("Paying close attention"),
|
||||
"expected attention note, got: {text}"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn smuggler_high_stress_identifies_distraction() {
|
||||
let text = generate_examine_text(NpcMood::Anxious, 80, CharacterArchetype::Smuggler, None);
|
||||
fn high_stress_identifies_pressure() {
|
||||
let text = generate_examine_text(NpcMood::Anxious, 80, None);
|
||||
assert!(
|
||||
text.contains("Too distracted"),
|
||||
"expected distraction read, got: {text}"
|
||||
text.contains("Under pressure"),
|
||||
"expected pressure read, got: {text}"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn detective_deceptive_npc_notes_concealment() {
|
||||
fn deceptive_npc_notes_concealment() {
|
||||
let t = traits(&[PersonalityTrait::Deceptive]);
|
||||
let text = generate_examine_text(
|
||||
NpcMood::Neutral,
|
||||
20,
|
||||
CharacterArchetype::Detective,
|
||||
Some(&t),
|
||||
);
|
||||
let text = generate_examine_text(NpcMood::Neutral, 20, Some(&t));
|
||||
assert!(
|
||||
text.contains("Controlled affect"),
|
||||
"expected concealment note, got: {text}"
|
||||
@@ -365,8 +337,8 @@ mod tests {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn detective_anxious_npc_notes_stress_markers() {
|
||||
let text = generate_examine_text(NpcMood::Anxious, 50, CharacterArchetype::Detective, None);
|
||||
fn anxious_npc_notes_stress_markers() {
|
||||
let text = generate_examine_text(NpcMood::Anxious, 50, None);
|
||||
assert!(
|
||||
text.contains("stress markers"),
|
||||
"expected stress markers, got: {text}"
|
||||
@@ -374,8 +346,8 @@ mod tests {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn detective_content_npc_notes_low_guard() {
|
||||
let text = generate_examine_text(NpcMood::Content, 10, CharacterArchetype::Detective, None);
|
||||
fn content_npc_notes_low_guard() {
|
||||
let text = generate_examine_text(NpcMood::Content, 10, None);
|
||||
assert!(
|
||||
text.contains("Less guarded"),
|
||||
"expected low guard note, got: {text}"
|
||||
|
||||
@@ -72,8 +72,6 @@ pub type EconomicModifier = String;
|
||||
pub type FactionModifier = String;
|
||||
/// Condition modifier on a zone palette (worn, pristine, damaged). Stub.
|
||||
pub type ConditionModifier = String;
|
||||
/// Heritage root modifier (Settled Reach cultural grammar layer). Stub.
|
||||
pub type HeritageRoot = String;
|
||||
/// Season modifier (affects palette and ambient conditions). Stub.
|
||||
pub type Season = String;
|
||||
/// Role slot within a social site template. Stub.
|
||||
@@ -366,7 +364,6 @@ pub enum PaletteModifier {
|
||||
Era(Era),
|
||||
FactionPresence(FactionModifier),
|
||||
Condition(ConditionModifier),
|
||||
Heritage(HeritageRoot),
|
||||
Season(Season),
|
||||
}
|
||||
|
||||
|
||||
@@ -131,8 +131,6 @@ pub struct MonologueState {
|
||||
pub entered: bool,
|
||||
/// IDs of lines already shown (dedup within session).
|
||||
pub shown_ids: BTreeSet<String>,
|
||||
/// Character type for pool filtering. Set from CharacterArchetype (#587).
|
||||
pub character: String,
|
||||
/// Tick of the last observation event we reacted to (#119, observe_npc).
|
||||
/// Observation events arrive one tick after the snapshot that caused them,
|
||||
/// so we track which tick's events we've already processed.
|
||||
@@ -147,8 +145,6 @@ impl Default for MonologueState {
|
||||
idle_ticks: 0,
|
||||
entered: false,
|
||||
shown_ids: BTreeSet::new(),
|
||||
// Default to detective; overridden by CharacterArchetype at spawn (#587)
|
||||
character: "detective".to_string(),
|
||||
last_observation_tick: 0,
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user