1extern crate alloc;
7
8use alloc::string::String;
9use alloc::vec::Vec;
10
11use crate::types::*;
12
13pub const CONSTRAINTS: &[&str] = &[
15 "CONSTRAINT 1: An agent cannot create a tile with confidence > 0.95 without empirical evidence",
16 "CONSTRAINT 2: An agent cannot claim equivalence without falsification",
17 "CONSTRAINT 3: An agent must cite tile dependencies before crafting",
18 "CONSTRAINT 4: An NPC must not give advice outside its expertise domain",
19 "CONSTRAINT 5: Room exits must preserve mathematical guarantees (covering radius, etc.)",
20 "CONSTRAINT 6: Zeitgeist merge must be commutative, associative, idempotent (CRDT)",
21 "CONSTRAINT 7: No room may operate without parity monitoring",
22 "CONSTRAINT 8: FLUX transference must carry full zeitgeist (not just payload)",
23];
24
25pub struct Deadband {
27 pub warning_threshold: f64,
29 pub block_threshold: f64,
31}
32
33impl Default for Deadband {
34 fn default() -> Self {
35 Self {
36 warning_threshold: 0.1,
37 block_threshold: 0.3,
38 }
39 }
40}
41
42pub struct AlignmentChecker {
44 deadband: Deadband,
45 violation_log: Vec<AlignmentReport>,
46}
47
48impl Default for AlignmentChecker {
49 fn default() -> Self {
50 Self::new()
51 }
52}
53
54impl AlignmentChecker {
55 pub fn new() -> Self {
56 Self {
57 deadband: Deadband::default(),
58 violation_log: Vec::new(),
59 }
60 }
61
62 pub fn check_command(
64 &self,
65 agent: &AgentId,
66 cmd: &Command,
67 engine: &crate::engine::Engine,
68 ) -> Result<(), String> {
69 match cmd {
70 Command::Craft(inputs) => {
71 for input in inputs {
73 if engine.get_tile(&TileId(input.clone())).is_none() {
74 return Err(format!(
76 "ALIGNMENT VIOLATION: Missing dependency '{}' (Constraint 3)",
77 input
78 ));
79 }
80 }
81 }
82 Command::Drop(_tile_id) => {
83 if let Some(session) = engine.get_session(agent) {
85 if let Some(room) = engine.get_room(&session.current_room) {
86 if room.domain == Domain::Alignment && room.tiles.len() <= 1 {
87 }
89 }
90 }
91 }
92 _ => {}
93 }
94 Ok(())
95 }
96
97 pub fn check_exit_constraint(&self, _source: &RoomId, _target: &RoomId) -> bool {
99 true
102 }
103
104 pub fn check_tile_creation(&mut self, tile: &Tile) -> Result<(), String> {
106 if tile.confidence > 0.95 {
108 match &tile.content {
109 TileContent::EmpiricalData(_) | TileContent::Benchmark(_) => {}
110 _ => {
111 self.log_violation(
112 1,
113 false,
114 format!(
115 "Tile '{}' has confidence {:.2} without evidence",
116 tile.title, tile.confidence
117 ),
118 AlignmentSeverity::Block,
119 );
120 return Err("ALIGNMENT VIOLATION: Constraint 1".into());
121 }
122 }
123 }
124
125 if let TileContent::Proof(ref content) = tile.content {
127 if content.contains("equivalent") || content.contains("Equivalent") {
128 let has_falsification = tile.links.iter().any(|dep_id| {
129 dep_id.0.contains("falsif")
131 });
132 if !has_falsification {
133 self.log_violation(
134 2,
135 false,
136 format!(
137 "Tile '{}' claims equivalence without falsification",
138 tile.title
139 ),
140 AlignmentSeverity::Warning,
141 );
142 }
143 }
144 }
145
146 Ok(())
147 }
148
149 pub fn check_zeitgeist_merge(
151 &self,
152 local: &Zeitgeist,
153 incoming: &Zeitgeist,
154 ) -> Result<(), String> {
155 let mut z1 = local.clone();
157 let _z2 = incoming.clone();
158 z1.merge(incoming);
159
160 let mut z3 = incoming.clone();
161 let _z4 = local.clone();
162 z3.merge(local);
163
164 let mut z5 = local.clone();
166 z5.merge(local);
167
168 Ok(())
171 }
172
173 fn log_violation(
175 &mut self,
176 constraint_id: u8,
177 passed: bool,
178 message: String,
179 severity: AlignmentSeverity,
180 ) {
181 self.violation_log.push(AlignmentReport {
182 constraint_id,
183 passed,
184 message,
185 severity,
186 });
187 }
188
189 pub fn violations(&self) -> &[AlignmentReport] {
191 &self.violation_log
192 }
193
194 pub fn deadband(&self) -> &Deadband {
196 &self.deadband
197 }
198
199 pub fn list_constraints() -> Vec<String> {
201 CONSTRAINTS.iter().map(|s| s.to_string()).collect()
202 }
203}
204
205#[cfg(test)]
206mod tests {
207 use super::*;
208
209 #[test]
210 fn test_list_constraints() {
211 let constraints = AlignmentChecker::list_constraints();
212 assert_eq!(constraints.len(), 8);
213 assert!(constraints[0].contains("confidence"));
214 assert!(constraints[7].contains("FLUX"));
215 }
216
217 #[test]
218 fn test_tile_creation_high_confidence_no_evidence() {
219 let mut checker = AlignmentChecker::new();
220 let tile = Tile {
221 id: TileId("t1".to_string()),
222 title: "Bad tile".to_string(),
223 location: SpatialIndex {
224 x: 0.0,
225 y: 0.0,
226 z: 0.0,
227 },
228 author: AgentId("agent".to_string()),
229 confidence: 0.99,
230 domain_tags: vec![],
231 links: vec![],
232 content: TileContent::Theorem("Some theorem".to_string()),
233 lifecycle: Lifecycle::Created,
234 bloom_hash: [0u8; 32],
235 };
236 assert!(checker.check_tile_creation(&tile).is_err());
237 assert_eq!(checker.violations().len(), 1);
238 }
239
240 #[test]
241 fn test_tile_creation_high_confidence_with_evidence() {
242 let mut checker = AlignmentChecker::new();
243 let tile = Tile {
244 id: TileId("t1".to_string()),
245 title: "Good tile".to_string(),
246 location: SpatialIndex {
247 x: 0.0,
248 y: 0.0,
249 z: 0.0,
250 },
251 author: AgentId("agent".to_string()),
252 confidence: 0.99,
253 domain_tags: vec![],
254 links: vec![],
255 content: TileContent::EmpiricalData("benchmarked at 42".to_string()),
256 lifecycle: Lifecycle::Created,
257 bloom_hash: [0u8; 32],
258 };
259 assert!(checker.check_tile_creation(&tile).is_ok());
260 }
261
262 #[test]
263 fn test_zeitgeist_merge_check() {
264 let checker = AlignmentChecker::new();
265 let z1 = Zeitgeist::new();
266 let z2 = Zeitgeist::new();
267 assert!(checker.check_zeitgeist_merge(&z1, &z2).is_ok());
268 }
269
270 #[test]
271 fn test_exit_constraint() {
272 let checker = AlignmentChecker::new();
273 assert!(checker.check_exit_constraint(&RoomId("a".to_string()), &RoomId("b".to_string())));
274 }
275}