feat(simulation): contraband detection + dialogue confidence gate (D-075) (#425, #524)

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:
2026-02-19 11:12:24 +01:00
co-authored by Claude Sonnet 4.6
parent 54754e4080
commit d103e445e7
12 changed files with 670 additions and 46 deletions
+8
View File
@@ -15,6 +15,7 @@ use crate::knowledge::{EntityRegistry, KnowledgeGraph, StableId};
use crate::perception::cognitive_delay::CognitiveDelay;
use crate::perception::query::{ActivePerceptionMode, VisibilityGeometry};
use crate::perception::vision_cone::Facing;
use crate::simulation::contraband::ScanEventBuffer;
use crate::simulation::dialogue::DialogueResponseBuffer;
use crate::simulation::interaction::NearbyInteractionBuffer;
use crate::simulation::inventory::{CarriedBy, InventorySlot, ItemName};
@@ -68,6 +69,7 @@ pub fn compute_observer_snapshot(
Option<&mut SprintAnomalyQueue>,
Option<&CognitiveDelay>,
Option<&mut DialogueResponseBuffer>,
Option<&mut ScanEventBuffer>,
),
With<PlayerCharacter>,
>,
@@ -92,6 +94,7 @@ pub fn compute_observer_snapshot(
mut anomaly_queue_opt,
cognitive_delay_opt,
mut dialogue_response_opt,
mut scan_event_buffer_opt,
)) = observer_query.single_mut()
else {
tracing::error!("compute_observer_snapshot: PlayerCharacter query failed");
@@ -168,6 +171,10 @@ pub fn compute_observer_snapshot(
let current_monologue = monologue_buffer.take();
let dialogue_response = dialogue_response_opt.as_mut().and_then(|buf| buf.take());
let scan_events = scan_event_buffer_opt
.as_mut()
.map(|buf| buf.take())
.unwrap_or_default();
// Build pending recognitions from CognitiveDelay (#423, D-060)
let pending_recognitions = cognitive_delay_opt
@@ -207,6 +214,7 @@ pub fn compute_observer_snapshot(
pending_recognitions,
dialogue_response,
blocked_entities,
scan_events,
});
}
+1 -4
View File
@@ -2106,10 +2106,7 @@ fn npc_behind_wall_appears_in_blocked_entities() {
"NPC behind wall should appear in blocked_entities"
);
// Not in visible entities
let npc_visible = snapshot
.entities
.iter()
.any(|e| e.entity_id == npc_sid.0);
let npc_visible = snapshot.entities.iter().any(|e| e.entity_id == npc_sid.0);
assert!(!npc_visible, "NPC should not be in visible entities");
}