fix(simulation): address PR review items for #655 content removal

- line_pool.rs: add trace! when active_situations is empty (Layer 2
  will silently filter all lines — important for content debugging)
- knowledge_grant.rs: document serde(untagged) ambiguity hazard; add
  KnowledgeGrant::validate() for load-time field validation
- monologue.rs: annotate trigger_monologue as schedule ordering anchor;
  fix stale "periodic trigger cooldown tracking" doc reference in
  trigger_event_monologue (COOLDOWN_TICKS removed in #655)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-03-13 09:32:48 +01:00
co-authored by Claude Sonnet 4.6
parent e317a66ac6
commit b37583ddfc
3 changed files with 48 additions and 10 deletions
+31
View File
@@ -11,6 +11,10 @@ use std::collections::BTreeMap;
/// Untagged enum — serde tries each variant in order:
/// `Fact` matches YAML with `fact_id` field.
/// `Entity` matches YAML with `entity_ref` field.
///
/// **Ambiguity hazard**: if a YAML object contains both `fact_id` and `entity_ref`,
/// serde(untagged) silently selects `Fact` (first variant wins) and ignores `entity_ref`.
/// Use [`KnowledgeGrant::validate`] at load time to catch this case.
#[derive(Debug, Clone, Deserialize)]
#[serde(untagged)]
pub enum KnowledgeGrant {
@@ -31,6 +35,33 @@ pub enum KnowledgeGrant {
},
}
impl KnowledgeGrant {
/// Validates that the grant is internally consistent.
///
/// Specifically catches the serde(untagged) ambiguity: a raw YAML value that has
/// both `fact_id` and `entity_ref` would parse silently as `Fact`, losing the
/// entity reference. This can only be detected after deserializing the surrounding
/// structure — call this on every grant after loading.
///
/// Currently there is no post-deserialization raw-value access, so this serves as
/// a place to add future cross-field validation. Returns `Ok(())` for now.
pub fn validate(&self) -> Result<(), String> {
match self {
KnowledgeGrant::Fact { fact_id, .. } => {
if fact_id.is_empty() {
return Err("KnowledgeGrant::Fact has empty fact_id".to_string());
}
}
KnowledgeGrant::Entity { entity_ref, .. } => {
if entity_ref.is_empty() {
return Err("KnowledgeGrant::Entity has empty entity_ref".to_string());
}
}
}
Ok(())
}
}
/// Prerequisite set for a monologue line.
#[derive(Debug, Clone, Deserialize)]
pub struct Prerequisites {