feat(schema): implement D-035 converged tag taxonomy as canonical JSON Schema (#168)

- Add content/_schema/dialogue-line.schema.json — canonical standalone line schema
  with all 6 structural + 3 selection + 2 authoring-only tags and $defs for
  monologue extension (character, trigger, prerequisite)
- Update dialogue-pool.schema.json: add dual_lens and notes authoring-only tags,
  add additionalProperties: false to knowledge_grant
- Update monologue-pool.schema.json: add all 6 structural tags as required fields
  for schema compliance (role=player_character, access=[public], trust=surface are
  constrained constants); add topic, mood, dual_lens, notes
- Enumerate all 14 v0.1 situations and 9 v0.1 topics as allowed enum values per D-035
- Fix Rust sync gap: add Situation::Greeting and Mood::Focused to line_pool.rs
  (D-035 Sprint 8 amendments — were in schema but missing from Rust enums, causing
  content with these values to be silently dropped at runtime)
- Update line_preview.rs situation_str, mood_str match arms and help text for
  new variants; all 25 line_pool tests pass

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-02-20 18:38:37 +01:00
co-authored by Claude Sonnet 4.6
parent 4e53a2e979
commit 8c362c8535
11 changed files with 3482 additions and 36 deletions
+3 -1
View File
@@ -348,7 +348,7 @@ fn run_dialogue(index: &LinePoolIndex, args: &Args) {
"situation",
"arrival, shift_start, shift_end, shift_transition, bar_evening, \
night_shift, investigation, confrontation, social, alone, \
emergency, routine, observation",
emergency, routine, observation, greeting",
)
})
.collect()
@@ -596,6 +596,7 @@ fn situation_str(s: &Situation) -> &'static str {
Situation::Emergency => "emergency",
Situation::Routine => "routine",
Situation::Observation => "observation",
Situation::Greeting => "greeting",
}
}
@@ -637,5 +638,6 @@ fn mood_str(m: &Mood) -> &'static str {
Mood::Conflicted => "conflicted",
Mood::Concerned => "concerned",
Mood::Relieved => "relieved",
Mood::Focused => "focused",
}
}
+14
View File
@@ -79,6 +79,9 @@ impl FromStr for TrustTier {
}
/// D-028 Layer 2: Situation context — when this line can fire.
///
/// 14 v0.1 values: 13 original + Greeting added Sprint 8 (D-035 amendment)
/// for PC dialogue pools initial contact lines.
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub enum Situation {
Arrival,
@@ -94,6 +97,8 @@ pub enum Situation {
Emergency,
Routine,
Observation,
/// Added Sprint 8 (D-035 amendment): PC dialogue initial contact lines.
Greeting,
}
impl FromStr for Situation {
@@ -113,6 +118,7 @@ impl FromStr for Situation {
"emergency" => Ok(Self::Emergency),
"routine" => Ok(Self::Routine),
"observation" => Ok(Self::Observation),
"greeting" => Ok(Self::Greeting),
_ => Err(ParseEnumError {
kind: "Situation",
value: s.to_string(),
@@ -157,6 +163,9 @@ impl FromStr for Topic {
}
/// D-028 Layer 4: Mood tag — influences weighted selection.
///
/// 9 v0.1 values: 8 original + Focused added Sprint 8 (D-035 amendment)
/// for Kael dialogue at The Terminal and maintenance corridors.
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub enum Mood {
Fond,
@@ -167,6 +176,8 @@ pub enum Mood {
Conflicted,
Concerned,
Relieved,
/// Added Sprint 8 (D-035 amendment): Kael dialogue at Terminal/corridors.
Focused,
}
impl FromStr for Mood {
@@ -181,6 +192,7 @@ impl FromStr for Mood {
"conflicted" => Ok(Self::Conflicted),
"concerned" => Ok(Self::Concerned),
"relieved" => Ok(Self::Relieved),
"focused" => Ok(Self::Focused),
_ => Err(ParseEnumError {
kind: "Mood",
value: s.to_string(),
@@ -664,6 +676,7 @@ mod tests {
"emergency",
"routine",
"observation",
"greeting", // Sprint 8 amendment (D-035)
];
for v in values {
assert!(
@@ -704,6 +717,7 @@ mod tests {
"conflicted",
"concerned",
"relieved",
"focused", // Sprint 8 amendment (D-035)
];
for v in values {
assert!(v.parse::<Mood>().is_ok(), "Failed to parse mood: {}", v);