Contraband detection (Task #425, D-065): - New module: simulation/contraband.rs — NPC scan checks carried items against the KnowledgeGraph confidence gate. NPCs with Authority access can initiate a scan; scan outcome depends on item CarriedBy + KG entry. - Adds ContrabandScanResult event type and ContrabanEntry component. - Wired into simulation/mod.rs module list. Dialogue confidence gate (Task #524, D-075 — OQ-18 resolution): - relationship_to_trust() gains confidence parameter (KnowledgeConfidence). - Trust tier mapping: (Friendly, KnowsDetails+)→Secret, (Friendly|Known, KnowsOf+)→Real, otherwise Surface. Access tier (Layer 1) unchanged. - Caller process_talk_interaction passes observer KG confidence_of target. - Resolves OQ-18: confidence co-gates TrustTier, not AccessTier. Supporting changes: - decisions/content.md: add D-075 (16 decisions, dated 2026-02-19) - knowledge/types.rs: expose KnowledgeConfidence comparison helpers - knowledge/registry.rs: minor API polish - bridge/types.rs: ContrabandScanResult wire type - bridge/text_renderer.rs: render contraband scan status - perception/observer: include carried item count in snapshot - npc/mod.rs: NPC scan range constant, authority flag Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -171,18 +171,27 @@ pub fn available_access_tiers(relationship: RelationshipState) -> Vec<AccessTier
|
||||
}
|
||||
}
|
||||
|
||||
/// Map RelationshipState to the player's effective TrustTier.
|
||||
/// Map RelationshipState + KnowledgeConfidence to the player's effective TrustTier.
|
||||
///
|
||||
/// v0.1 mapping:
|
||||
/// - Friendly → Real (relationship depth unlocks deeper trust)
|
||||
/// - All others → Surface
|
||||
///
|
||||
/// TODO: TrustTier::Secret is currently unreachable. It should gate on
|
||||
/// KG confidence (e.g., KnowsDetails+ for a specific secret topic) rather
|
||||
/// than RelationshipState alone. Tracked for Phase 2 narrative expansion.
|
||||
pub fn relationship_to_trust(relationship: RelationshipState) -> TrustTier {
|
||||
/// D-075 layered gate: trust requires BOTH relationship depth AND knowledge depth.
|
||||
/// - Secret: Friendly + KnowsDetails+ (deep rapport + actionable knowledge)
|
||||
/// - Real: (Friendly or Known) + KnowsOf+ (rapport + substantive knowledge)
|
||||
/// - Surface: everything else (baseline, always available)
|
||||
pub fn relationship_to_trust(
|
||||
relationship: RelationshipState,
|
||||
confidence: crate::knowledge::types::KnowledgeConfidence,
|
||||
) -> TrustTier {
|
||||
use crate::knowledge::types::KnowledgeConfidence;
|
||||
|
||||
match relationship {
|
||||
RelationshipState::Friendly => TrustTier::Real,
|
||||
RelationshipState::Friendly if confidence >= KnowledgeConfidence::KnowsDetails => {
|
||||
TrustTier::Secret
|
||||
}
|
||||
RelationshipState::Friendly | RelationshipState::Known
|
||||
if confidence >= KnowledgeConfidence::KnowsOf =>
|
||||
{
|
||||
TrustTier::Real
|
||||
}
|
||||
_ => TrustTier::Surface,
|
||||
}
|
||||
}
|
||||
@@ -373,8 +382,11 @@ pub fn process_talk_interaction(
|
||||
// Layer 2: Derive active situations from game state
|
||||
let situations = derive_situations(time.day_phase(), relationship);
|
||||
|
||||
// Layer 3: Trust tier from relationship
|
||||
let trust = relationship_to_trust(relationship);
|
||||
// Layer 3: Trust tier from relationship + confidence (D-075)
|
||||
let confidence = target_stable
|
||||
.and_then(|sid| observer_kg.confidence_of(&sid))
|
||||
.unwrap_or(crate::knowledge::types::KnowledgeConfidence::Suspects);
|
||||
let trust = relationship_to_trust(relationship, confidence);
|
||||
|
||||
// Query Layers 1-3: collect candidates across all available access tiers
|
||||
let mut candidates: Vec<&IndexedDialogueLine> = Vec::new();
|
||||
@@ -565,10 +577,7 @@ const CONFRONTATION_LINES: &[(&str, &str)] = &[
|
||||
"confront_01",
|
||||
"That changed everything between us. No going back.",
|
||||
),
|
||||
(
|
||||
"confront_02",
|
||||
"The look on their face... they know I know.",
|
||||
),
|
||||
("confront_02", "The look on their face... they know I know."),
|
||||
(
|
||||
"confront_03",
|
||||
"Cards on the table. Let's see what happens next.",
|
||||
@@ -600,13 +609,8 @@ pub fn process_confrontation_response(
|
||||
With<PlayerCharacter>,
|
||||
>,
|
||||
) {
|
||||
let Ok((
|
||||
player_entity,
|
||||
confrontation,
|
||||
mut observer_kg,
|
||||
mut monologue_buf,
|
||||
mut monologue_state,
|
||||
)) = query.single_mut()
|
||||
let Ok((player_entity, confrontation, mut observer_kg, mut monologue_buf, mut monologue_state)) =
|
||||
query.single_mut()
|
||||
else {
|
||||
return;
|
||||
};
|
||||
@@ -714,26 +718,83 @@ mod tests {
|
||||
assert_eq!(tiers, vec![AccessTier::Hostile]);
|
||||
}
|
||||
|
||||
// -- Trust tier tests (D-075: layered confidence gate) --------------------
|
||||
|
||||
#[test]
|
||||
fn trust_friendly_is_real() {
|
||||
fn trust_friendly_knows_details_is_secret() {
|
||||
use crate::knowledge::types::KnowledgeConfidence;
|
||||
assert_eq!(
|
||||
relationship_to_trust(RelationshipState::Friendly),
|
||||
relationship_to_trust(
|
||||
RelationshipState::Friendly,
|
||||
KnowledgeConfidence::KnowsDetails
|
||||
),
|
||||
TrustTier::Secret
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn trust_friendly_direct_is_secret() {
|
||||
use crate::knowledge::types::KnowledgeConfidence;
|
||||
assert_eq!(
|
||||
relationship_to_trust(RelationshipState::Friendly, KnowledgeConfidence::Direct),
|
||||
TrustTier::Secret
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn trust_friendly_knows_of_is_real() {
|
||||
use crate::knowledge::types::KnowledgeConfidence;
|
||||
assert_eq!(
|
||||
relationship_to_trust(RelationshipState::Friendly, KnowledgeConfidence::KnowsOf),
|
||||
TrustTier::Real
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn trust_others_are_surface() {
|
||||
fn trust_friendly_suspects_is_surface() {
|
||||
use crate::knowledge::types::KnowledgeConfidence;
|
||||
assert_eq!(
|
||||
relationship_to_trust(RelationshipState::Unknown),
|
||||
relationship_to_trust(RelationshipState::Friendly, KnowledgeConfidence::Suspects),
|
||||
TrustTier::Surface
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn trust_known_knows_of_is_real() {
|
||||
use crate::knowledge::types::KnowledgeConfidence;
|
||||
assert_eq!(
|
||||
relationship_to_trust(RelationshipState::Known),
|
||||
relationship_to_trust(RelationshipState::Known, KnowledgeConfidence::KnowsOf),
|
||||
TrustTier::Real
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn trust_known_suspects_is_surface() {
|
||||
use crate::knowledge::types::KnowledgeConfidence;
|
||||
assert_eq!(
|
||||
relationship_to_trust(RelationshipState::Known, KnowledgeConfidence::Suspects),
|
||||
TrustTier::Surface
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn trust_unknown_is_always_surface() {
|
||||
use crate::knowledge::types::KnowledgeConfidence;
|
||||
assert_eq!(
|
||||
relationship_to_trust(RelationshipState::PersonOfInterest),
|
||||
relationship_to_trust(RelationshipState::Unknown, KnowledgeConfidence::Direct),
|
||||
TrustTier::Surface
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn trust_poi_is_always_surface() {
|
||||
use crate::knowledge::types::KnowledgeConfidence;
|
||||
// PersonOfInterest uses Authority access, not trust depth
|
||||
assert_eq!(
|
||||
relationship_to_trust(
|
||||
RelationshipState::PersonOfInterest,
|
||||
KnowledgeConfidence::KnowsDetails
|
||||
),
|
||||
TrustTier::Surface
|
||||
);
|
||||
}
|
||||
@@ -1576,7 +1637,10 @@ mod tests {
|
||||
DeviationTrigger::WalkAway,
|
||||
"Deviation trigger should be WalkAway"
|
||||
);
|
||||
assert_eq!(deviation.tick, 42, "Deviation should record the walk-away tick");
|
||||
assert_eq!(
|
||||
deviation.tick, 42,
|
||||
"Deviation should record the walk-away tick"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -1659,7 +1723,10 @@ mod tests {
|
||||
|
||||
// RoutineDeviation should be recorded (symmetric with walk-away)
|
||||
let deviation = world.get::<crate::npc::RoutineDeviation>(npc);
|
||||
assert!(deviation.is_some(), "NPC should get RoutineDeviation after confrontation");
|
||||
assert!(
|
||||
deviation.is_some(),
|
||||
"NPC should get RoutineDeviation after confrontation"
|
||||
);
|
||||
assert_eq!(
|
||||
deviation.unwrap().trigger,
|
||||
crate::npc::DeviationTrigger::Confrontation,
|
||||
|
||||
Reference in New Issue
Block a user