split_brain_harness/
soul.rs1use crate::types::Soul;
2use anyhow::{anyhow, Result};
3
4const DEFAULT_SOUL: &str = include_str!("../soul.md");
5
6const OPEN_LOGIC: &str = "[LOGIC_SYSTEM_PROMPT]";
7const CLOSE_LOGIC: &str = "[/LOGIC_SYSTEM_PROMPT]";
8const OPEN_CREATIVE: &str = "[CREATIVE_SYSTEM_PROMPT]";
9const CLOSE_CREATIVE: &str = "[/CREATIVE_SYSTEM_PROMPT]";
10const OPEN_VERIFIER: &str = "[VERIFIER_SYSTEM_PROMPT]";
11const CLOSE_VERIFIER: &str = "[/VERIFIER_SYSTEM_PROMPT]";
12const OPEN_CODE_GEN: &str = "[CODE_GEN_SYSTEM_PROMPT]";
13const CLOSE_CODE_GEN: &str = "[/CODE_GEN_SYSTEM_PROMPT]";
14
15pub fn load(path: Option<&str>) -> Result<Soul> {
17 let raw = match path {
18 Some(p) if !p.is_empty() => std::fs::read_to_string(p)
19 .map_err(|e| anyhow!("failed to read soul file '{}': {}", p, e))?,
20 _ => DEFAULT_SOUL.to_string(),
21 };
22 parse(&raw)
23}
24
25pub fn wrap_payload(input: &str) -> String {
27 format!("<payload>\n{}\n</payload>", input)
28}
29
30pub fn wrap_verifier_payload(original_input: &str, proposed_analysis: &str) -> String {
32 format!(
33 "<original_input>\n{}\n</original_input>\n<proposed_analysis>\n{}\n</proposed_analysis>",
34 original_input, proposed_analysis
35 )
36}
37
38fn parse(raw: &str) -> Result<Soul> {
43 Ok(Soul {
44 logic_system_prompt: extract(raw, OPEN_LOGIC, CLOSE_LOGIC)?,
45 creative_system_prompt: extract(raw, OPEN_CREATIVE, CLOSE_CREATIVE).unwrap_or_default(),
46 verifier_system_prompt: extract(raw, OPEN_VERIFIER, CLOSE_VERIFIER).unwrap_or_default(),
47 code_gen_system_prompt: extract(raw, OPEN_CODE_GEN, CLOSE_CODE_GEN).unwrap_or_default(),
48 })
49}
50
51fn extract(raw: &str, open: &str, close: &str) -> Result<String> {
52 let start = raw
53 .find(open)
54 .ok_or_else(|| anyhow!("soul.md missing opening tag: {}", open))?
55 + open.len();
56
57 let end = raw[start..]
58 .find(close)
59 .ok_or_else(|| anyhow!("soul.md missing closing tag: {}", close))?
60 + start;
61
62 Ok(raw[start..end].trim().to_string())
63}
64
65#[cfg(test)]
70mod tests {
71 use super::*;
72
73 #[test]
74 fn default_soul_parses() {
75 let soul = load(None).expect("default soul should parse");
76 assert!(
77 !soul.logic_system_prompt.is_empty(),
78 "logic prompt must not be empty"
79 );
80 assert!(
81 soul.logic_system_prompt.contains("telemetry engine"),
82 "logic prompt should contain identity marker"
83 );
84 assert!(
85 !soul.verifier_system_prompt.is_empty(),
86 "verifier prompt must not be empty"
87 );
88 assert!(
89 soul.verifier_system_prompt.contains("claim verifier"),
90 "verifier prompt should contain identity marker"
91 );
92 }
93
94 #[test]
95 fn wrap_payload_contains_tags() {
96 let wrapped = wrap_payload("hello world");
97 assert!(wrapped.contains("<payload>"));
98 assert!(wrapped.contains("</payload>"));
99 assert!(wrapped.contains("hello world"));
100 }
101
102 #[test]
103 fn wrap_verifier_payload_contains_both_sections() {
104 let wrapped = wrap_verifier_payload("raw input", r#"{"foo":"bar"}"#);
105 assert!(wrapped.contains("<original_input>"));
106 assert!(wrapped.contains("raw input"));
107 assert!(wrapped.contains("<proposed_analysis>"));
108 assert!(wrapped.contains(r#"{"foo":"bar"}"#));
109 }
110
111 #[test]
112 fn extract_section_trims_whitespace() {
113 let raw = "[LOGIC_SYSTEM_PROMPT]\n content \n[/LOGIC_SYSTEM_PROMPT]";
114 let result = extract(raw, "[LOGIC_SYSTEM_PROMPT]", "[/LOGIC_SYSTEM_PROMPT]").unwrap();
115 assert_eq!(result, "content");
116 }
117
118 #[test]
119 fn missing_close_tag_errors() {
120 let raw = "[LOGIC_SYSTEM_PROMPT]\ncontent";
121 assert!(extract(raw, "[LOGIC_SYSTEM_PROMPT]", "[/LOGIC_SYSTEM_PROMPT]").is_err());
122 }
123}