fix(simulation): address PR #89 review — version comment, settings contract, docs, dead process recovery

Must-fix: protocol version comment now references PROTOCOL_VERSION
(no hardcoded number), settings delete is idempotent no-op.

Suggestions addressed: BehaviorModifier dedup claim dropped, hash
collision safety documented, FK pragma in test store, columns_to_value
consolidated, modifier_hint mismatch logging, OffDuty test coverage,
batching tradeoff documented, unknown value_type warning, occluded
text risk documented, dead child respawn in voice worker, INJECT
block ambiguity documented.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-03-13 09:34:33 +01:00
co-authored by Claude Opus 4.6
parent b37583ddfc
commit 51414b0b63
7 changed files with 146 additions and 38 deletions
+34 -1
View File
@@ -158,7 +158,6 @@ impl Default for BehaviorContext {
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct BehaviorModifier {
/// Modifier category for matching against `BehaviorPrimitive.modifier_hint`.
/// Also used for dedup — at most one modifier per category per NPC.
pub category: String,
/// The modifier clause text. Concatenated to action text with a space.
pub clause: String,
@@ -197,6 +196,10 @@ pub fn assemble_behaviors(
let hinted: Vec<&BehaviorModifier> =
modifiers.iter().filter(|m| &m.category == hint).collect();
if hinted.is_empty() {
tracing::trace!(
"modifier_hint '{}' matched no category; falling back to all modifiers",
hint
);
modifiers.iter().collect()
} else {
hinted
@@ -674,6 +677,36 @@ mod tests {
assert_eq!(all.len(), 3);
}
#[test]
fn assemble_behaviors_off_duty_context_filter() {
use crate::simulation::rng::SimRng;
let mut rng = SimRng::new(42);
let primitives = vec![
BehaviorPrimitive {
action: "operates crane".into(),
context: BehaviorContext::OnShift,
modifier_hint: None,
},
BehaviorPrimitive {
action: "sleeps in bunk".into(),
context: BehaviorContext::OffDuty,
modifier_hint: None,
},
BehaviorPrimitive {
action: "stretches".into(),
context: BehaviorContext::Any,
modifier_hint: None,
},
];
// OffDuty filter: should return OffDuty + Any, exclude OnShift
let off_duty = assemble_behaviors(&primitives, &[], Some(BehaviorContext::OffDuty), &mut rng);
assert_eq!(off_duty.len(), 2);
assert_eq!(off_duty[0], "sleeps in bunk");
assert_eq!(off_duty[1], "stretches");
}
#[test]
fn assemble_behaviors_hint_fallback_to_any_modifier() {
use crate::simulation::rng::SimRng;