1use super::brutalhonesty::{
37 BrutalHonesty, BrutalHonestyConfig, CritiqueSeverity, CritiqueVerdict, FlawSeverity,
38};
39use super::{ThinkToolContext, ThinkToolModule, ThinkToolModuleConfig, ThinkToolOutput};
40use crate::error::{Error, Result};
41use serde::{Deserialize, Serialize};
42use serde_json::json;
43
44#[derive(Debug, Clone, Copy, PartialEq, Eq, Default, Serialize, Deserialize)]
46#[serde(rename_all = "snake_case")]
47pub enum CognitiveBiasDepth {
48 Basic,
50 #[default]
52 Standard,
53 Deep,
55}
56
57#[derive(Debug, Clone, Serialize, Deserialize)]
59pub struct CognitiveBias {
60 pub name: String,
62 pub category: BiasCategory,
64 pub manifestation: String,
66 pub confidence: f64,
68 pub debiasing_strategy: String,
70}
71
72#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
74#[serde(rename_all = "snake_case")]
75pub enum BiasCategory {
76 DecisionMaking,
78 Social,
80 Memory,
82 Probability,
84 SelfRelated,
86}
87
88#[derive(Debug, Clone, Serialize, Deserialize)]
90pub struct CulturalAssumption {
91 pub assumption: String,
93 pub context: String,
95 pub exceptions: Vec<String>,
97 pub risk_level: FlawSeverity,
99}
100
101#[derive(Debug, Clone, Serialize, Deserialize)]
103pub struct SteelmanArgument {
104 pub argument: String,
106 pub premises: Vec<String>,
108 pub challenge: String,
110 pub strength: f64,
112}
113
114#[derive(Debug, Clone, Serialize, Deserialize)]
116pub struct ArgumentMap {
117 pub claim: String,
119 pub premises: Vec<String>,
121 pub evidence: Vec<String>,
123 pub warrants: Vec<String>,
125 pub qualifiers: Vec<String>,
127 pub rebuttals: Vec<String>,
129}
130
131#[derive(Debug, Clone, Serialize, Deserialize)]
133pub struct EnhancedConfig {
134 pub base_config: BrutalHonestyConfig,
136 pub enable_cultural_analysis: bool,
138 pub enable_steelmanning: bool,
140 pub enable_argument_mapping: bool,
142 pub cognitive_bias_depth: CognitiveBiasDepth,
144 pub target_cultures: Vec<String>,
146}
147
148impl Default for EnhancedConfig {
149 fn default() -> Self {
150 Self {
151 base_config: BrutalHonestyConfig::default(),
152 enable_cultural_analysis: true,
153 enable_steelmanning: true,
154 enable_argument_mapping: true,
155 cognitive_bias_depth: CognitiveBiasDepth::Standard,
156 target_cultures: vec![
157 "Western".to_string(),
158 "East Asian".to_string(),
159 "South Asian".to_string(),
160 "Middle Eastern".to_string(),
161 "Latin American".to_string(),
162 ],
163 }
164 }
165}
166
167#[derive(Debug, Default)]
169pub struct EnhancedBuilder {
170 config: EnhancedConfig,
171}
172
173impl EnhancedBuilder {
174 pub fn new() -> Self {
176 Self::default()
177 }
178
179 pub fn severity(mut self, severity: CritiqueSeverity) -> Self {
181 self.config.base_config.severity = severity;
182 self
183 }
184
185 pub fn enable_cultural_analysis(mut self, enable: bool) -> Self {
187 self.config.enable_cultural_analysis = enable;
188 self
189 }
190
191 pub fn enable_steelmanning(mut self, enable: bool) -> Self {
193 self.config.enable_steelmanning = enable;
194 self
195 }
196
197 pub fn enable_argument_mapping(mut self, enable: bool) -> Self {
199 self.config.enable_argument_mapping = enable;
200 self
201 }
202
203 pub fn cognitive_bias_depth(mut self, depth: CognitiveBiasDepth) -> Self {
205 self.config.cognitive_bias_depth = depth;
206 self
207 }
208
209 pub fn target_cultures(mut self, cultures: Vec<String>) -> Self {
211 self.config.target_cultures = cultures;
212 self
213 }
214
215 pub fn build(self) -> BrutalHonestyEnhanced {
217 BrutalHonestyEnhanced::with_config(self.config)
218 }
219}
220
221pub struct BrutalHonestyEnhanced {
223 module_config: ThinkToolModuleConfig,
225 config: EnhancedConfig,
227 base_module: BrutalHonesty,
229}
230
231impl Default for BrutalHonestyEnhanced {
232 fn default() -> Self {
233 Self::new()
234 }
235}
236
237impl BrutalHonestyEnhanced {
238 pub fn new() -> Self {
240 Self::with_config(EnhancedConfig::default())
241 }
242
243 pub fn with_config(config: EnhancedConfig) -> Self {
245 let base_module = BrutalHonesty::with_config(config.base_config.clone());
246
247 Self {
248 module_config: ThinkToolModuleConfig {
249 name: "BrutalHonestyEnhanced".to_string(),
250 version: "3.0.0".to_string(),
251 description:
252 "Enhanced adversarial critique with cognitive bias detection and steelmanning"
253 .to_string(),
254 confidence_weight: 0.18, },
256 config,
257 base_module,
258 }
259 }
260
261 pub fn builder() -> EnhancedBuilder {
263 EnhancedBuilder::new()
264 }
265
266 pub fn enhanced_config(&self) -> &EnhancedConfig {
268 &self.config
269 }
270
271 fn detect_cognitive_biases(&self, query: &str) -> Vec<CognitiveBias> {
273 let mut biases = Vec::new();
274 let query_lower = query.to_lowercase();
275
276 let basic_biases = self.get_basic_biases(&query_lower);
278 biases.extend(basic_biases);
279
280 if matches!(
281 self.config.cognitive_bias_depth,
282 CognitiveBiasDepth::Standard | CognitiveBiasDepth::Deep
283 ) {
284 let standard_biases = self.get_standard_biases(&query_lower);
285 biases.extend(standard_biases);
286 }
287
288 if matches!(self.config.cognitive_bias_depth, CognitiveBiasDepth::Deep) {
289 let deep_biases = self.get_deep_biases(&query_lower);
290 biases.extend(deep_biases);
291 }
292
293 biases
294 }
295
296 fn get_basic_biases(&self, query: &str) -> Vec<CognitiveBias> {
298 let mut biases = Vec::new();
299
300 if query.contains("proves")
302 || query.contains("confirms")
303 || (query.contains("evidence") && !query.contains("counter"))
304 {
305 biases.push(CognitiveBias {
306 name: "Confirmation Bias".to_string(),
307 category: BiasCategory::DecisionMaking,
308 manifestation: "Seeking or interpreting information to confirm existing beliefs"
309 .to_string(),
310 confidence: 0.70,
311 debiasing_strategy:
312 "Actively seek disconfirming evidence; assign someone to argue the opposite"
313 .to_string(),
314 });
315 }
316
317 if query.contains("first") || query.contains("initial") || query.contains("started with") {
319 biases.push(CognitiveBias {
320 name: "Anchoring Bias".to_string(),
321 category: BiasCategory::DecisionMaking,
322 manifestation: "Over-relying on initial information as reference point".to_string(),
323 confidence: 0.55,
324 debiasing_strategy:
325 "Consider multiple starting points; recalculate from different anchors"
326 .to_string(),
327 });
328 }
329
330 if query.contains("recently") || query.contains("just saw") || query.contains("in the news")
332 {
333 biases.push(CognitiveBias {
334 name: "Availability Heuristic".to_string(),
335 category: BiasCategory::Memory,
336 manifestation: "Overweighting easily recalled examples".to_string(),
337 confidence: 0.65,
338 debiasing_strategy:
339 "Seek base rate statistics; consider examples from multiple time periods"
340 .to_string(),
341 });
342 }
343
344 if query.contains("certain") || query.contains("definitely") || query.contains("no doubt") {
346 biases.push(CognitiveBias {
347 name: "Overconfidence Bias".to_string(),
348 category: BiasCategory::SelfRelated,
349 manifestation: "Excessive confidence in own judgment or predictions".to_string(),
350 confidence: 0.75,
351 debiasing_strategy: "Create prediction intervals; track calibration over time"
352 .to_string(),
353 });
354 }
355
356 if query.contains("already invested")
358 || query.contains("too far to stop")
359 || query.contains("wasted if")
360 {
361 biases.push(CognitiveBias {
362 name: "Sunk Cost Fallacy".to_string(),
363 category: BiasCategory::DecisionMaking,
364 manifestation: "Continuing due to past investment rather than future value"
365 .to_string(),
366 confidence: 0.80,
367 debiasing_strategy: "Evaluate decisions based only on future costs and benefits"
368 .to_string(),
369 });
370 }
371
372 biases
373 }
374
375 fn get_standard_biases(&self, query: &str) -> Vec<CognitiveBias> {
377 let mut biases = Vec::new();
378
379 if query.contains("should have known")
381 || query.contains("was obvious")
382 || query.contains("predictable")
383 {
384 biases.push(CognitiveBias {
385 name: "Hindsight Bias".to_string(),
386 category: BiasCategory::Memory,
387 manifestation: "Believing past events were more predictable than they were"
388 .to_string(),
389 confidence: 0.70,
390 debiasing_strategy:
391 "Document predictions before outcomes; review original uncertainty".to_string(),
392 });
393 }
394
395 if query.contains("everyone") || query.contains("popular") || query.contains("trend") {
397 biases.push(CognitiveBias {
398 name: "Bandwagon Effect".to_string(),
399 category: BiasCategory::Social,
400 manifestation: "Following the crowd rather than independent analysis".to_string(),
401 confidence: 0.60,
402 debiasing_strategy: "Evaluate based on fundamentals; consider contrarian positions"
403 .to_string(),
404 });
405 }
406
407 if query.contains("always done") || query.contains("traditional") || query.contains("usual")
409 {
410 biases.push(CognitiveBias {
411 name: "Status Quo Bias".to_string(),
412 category: BiasCategory::DecisionMaking,
413 manifestation: "Preferring current state regardless of merit".to_string(),
414 confidence: 0.65,
415 debiasing_strategy: "Explicitly compare status quo costs vs change benefits"
416 .to_string(),
417 });
418 }
419
420 if query.contains("they failed because they") || query.contains("their fault") {
422 biases.push(CognitiveBias {
423 name: "Fundamental Attribution Error".to_string(),
424 category: BiasCategory::Social,
425 manifestation: "Over-attributing behavior to personality vs situation".to_string(),
426 confidence: 0.55,
427 debiasing_strategy:
428 "Consider situational factors; ask 'what would I do in their position?'"
429 .to_string(),
430 });
431 }
432
433 if query.contains("will succeed")
435 || query.contains("bound to")
436 || query.contains("can't fail")
437 {
438 biases.push(CognitiveBias {
439 name: "Optimism Bias".to_string(),
440 category: BiasCategory::SelfRelated,
441 manifestation: "Overestimating likelihood of positive outcomes".to_string(),
442 confidence: 0.70,
443 debiasing_strategy: "Conduct pre-mortem analysis; use reference class forecasting"
444 .to_string(),
445 });
446 }
447
448 if query.contains("savings") || query.contains("loss") || query.contains("gain") {
450 biases.push(CognitiveBias {
451 name: "Framing Effect".to_string(),
452 category: BiasCategory::DecisionMaking,
453 manifestation: "Decision influenced by how information is presented".to_string(),
454 confidence: 0.50,
455 debiasing_strategy: "Reframe the problem multiple ways; use absolute numbers"
456 .to_string(),
457 });
458 }
459
460 biases
461 }
462
463 fn get_deep_biases(&self, query: &str) -> Vec<CognitiveBias> {
465 let mut biases = Vec::new();
466
467 if query.contains("estimate") || query.contains("timeline") || query.contains("schedule") {
469 biases.push(CognitiveBias {
470 name: "Planning Fallacy".to_string(),
471 category: BiasCategory::Probability,
472 manifestation: "Underestimating time, costs, and risks".to_string(),
473 confidence: 0.75,
474 debiasing_strategy: "Use reference class forecasting; add buffer based on past projects".to_string(),
475 });
476 }
477
478 if query.contains("successful companies")
480 || query.contains("winners")
481 || query.contains("examples of success")
482 {
483 biases.push(CognitiveBias {
484 name: "Survivorship Bias".to_string(),
485 category: BiasCategory::Probability,
486 manifestation: "Focusing on survivors while ignoring failures".to_string(),
487 confidence: 0.65,
488 debiasing_strategy: "Study failures; consider base rates of success vs failure"
489 .to_string(),
490 });
491 }
492
493 if query.contains("story") || query.contains("journey") || query.contains("narrative") {
495 biases.push(CognitiveBias {
496 name: "Narrative Fallacy".to_string(),
497 category: BiasCategory::Memory,
498 manifestation: "Creating false causal chains in retrospect".to_string(),
499 confidence: 0.55,
500 debiasing_strategy: "Focus on data; be skeptical of neat explanations".to_string(),
501 });
502 }
503
504 if query.contains("simple") || query.contains("easy") || query.contains("just need to") {
506 biases.push(CognitiveBias {
507 name: "Dunning-Kruger Effect".to_string(),
508 category: BiasCategory::SelfRelated,
509 manifestation: "Overestimating competence in areas of limited knowledge"
510 .to_string(),
511 confidence: 0.50,
512 debiasing_strategy: "Consult domain experts; acknowledge knowledge gaps"
513 .to_string(),
514 });
515 }
516
517 if query.contains("feel") || query.contains("gut") || query.contains("intuition") {
519 biases.push(CognitiveBias {
520 name: "Affect Heuristic".to_string(),
521 category: BiasCategory::DecisionMaking,
522 manifestation: "Letting emotions drive risk/benefit judgments".to_string(),
523 confidence: 0.60,
524 debiasing_strategy: "Separate emotional response from analytical evaluation"
525 .to_string(),
526 });
527 }
528
529 biases
530 }
531
532 fn detect_cultural_assumptions(&self, query: &str) -> Vec<CulturalAssumption> {
534 if !self.config.enable_cultural_analysis {
535 return Vec::new();
536 }
537
538 let mut assumptions = Vec::new();
539 let query_lower = query.to_lowercase();
540
541 if query_lower.contains("individual")
543 || query_lower.contains("personal achievement")
544 || query_lower.contains("self-made")
545 {
546 assumptions.push(CulturalAssumption {
547 assumption: "Individual achievement and autonomy are primary values".to_string(),
548 context: "Western, particularly American, cultural context".to_string(),
549 exceptions: vec![
550 "East Asian cultures (group harmony)".to_string(),
551 "Latin American cultures (family ties)".to_string(),
552 "African cultures (community focus)".to_string(),
553 ],
554 risk_level: FlawSeverity::Moderate,
555 });
556 }
557
558 if query_lower.contains("straightforward")
560 || query_lower.contains("direct")
561 || query_lower.contains("clear communication")
562 {
563 assumptions.push(CulturalAssumption {
564 assumption: "Direct, explicit communication is preferable".to_string(),
565 context: "Low-context cultures (Northern European, American)".to_string(),
566 exceptions: vec![
567 "High-context cultures (Japan, China)".to_string(),
568 "Middle Eastern cultures".to_string(),
569 "Many Asian cultures".to_string(),
570 ],
571 risk_level: FlawSeverity::Minor,
572 });
573 }
574
575 if query_lower.contains("deadline")
577 || query_lower.contains("punctual")
578 || query_lower.contains("time is money")
579 {
580 assumptions.push(CulturalAssumption {
581 assumption: "Linear, monochronic view of time".to_string(),
582 context: "Northern European, North American cultures".to_string(),
583 exceptions: vec![
584 "Polychronic cultures (Mediterranean, Latin America)".to_string(),
585 "Relationship-focused cultures".to_string(),
586 ],
587 risk_level: FlawSeverity::Minor,
588 });
589 }
590
591 if query_lower.contains("flat organization")
593 || query_lower.contains("anyone can")
594 || query_lower.contains("speak up")
595 {
596 assumptions.push(CulturalAssumption {
597 assumption: "Low power distance is desirable".to_string(),
598 context: "Scandinavian, Dutch, American cultures".to_string(),
599 exceptions: vec![
600 "High power distance cultures (Japan, India)".to_string(),
601 "Traditional hierarchical societies".to_string(),
602 ],
603 risk_level: FlawSeverity::Moderate,
604 });
605 }
606
607 assumptions
608 }
609
610 fn generate_steelman(&self, query: &str) -> Option<SteelmanArgument> {
612 if !self.config.enable_steelmanning {
613 return None;
614 }
615
616 let query_lower = query.to_lowercase();
617
618 if query_lower.contains("should")
620 || query_lower.contains("must")
621 || query_lower.contains("need to")
622 {
623 Some(SteelmanArgument {
624 argument: "The strongest case against this action is that the opportunity costs and risks may outweigh the benefits, and that the current state, while imperfect, has proven stability.".to_string(),
625 premises: vec![
626 "Change carries inherent risk and transition costs".to_string(),
627 "Status quo has survived past challenges".to_string(),
628 "Resources spent here cannot be used elsewhere".to_string(),
629 ],
630 challenge: "This challenges whether the proposed action is truly necessary or optimal given alternatives".to_string(),
631 strength: 0.70,
632 })
633 } else if query_lower.contains("will succeed") || query_lower.contains("will work") {
634 Some(SteelmanArgument {
635 argument: "Even well-planned initiatives with strong teams fail due to unforeseeable market shifts, timing issues, or execution challenges that compound unexpectedly.".to_string(),
636 premises: vec![
637 "Base rates of success are often lower than expected".to_string(),
638 "External factors can override internal capabilities".to_string(),
639 "Complex systems have emergent failure modes".to_string(),
640 ],
641 challenge: "This challenges the assumption that good inputs guarantee good outcomes".to_string(),
642 strength: 0.75,
643 })
644 } else if query_lower.contains("better") || query_lower.contains("best") {
645 Some(SteelmanArgument {
646 argument: "What is 'better' depends on criteria that may differ across stakeholders, time horizons, and contexts. The supposedly inferior alternative may optimize for different, equally valid goals.".to_string(),
647 premises: vec![
648 "Value judgments depend on evaluation criteria".to_string(),
649 "Stakeholders have different preferences".to_string(),
650 "Trade-offs are inherent in most choices".to_string(),
651 ],
652 challenge: "This challenges whether the comparison criteria are appropriate and complete".to_string(),
653 strength: 0.65,
654 })
655 } else {
656 Some(SteelmanArgument {
657 argument: "A sophisticated opponent would argue that this position overlooks key factors, relies on assumptions that may not hold, and underestimates alternatives.".to_string(),
658 premises: vec![
659 "Complex issues have multiple valid perspectives".to_string(),
660 "Our information may be incomplete".to_string(),
661 "Intelligent people disagree for good reasons".to_string(),
662 ],
663 challenge: "This challenges the completeness and robustness of the original argument".to_string(),
664 strength: 0.60,
665 })
666 }
667 }
668
669 fn build_argument_map(&self, query: &str) -> Option<ArgumentMap> {
671 if !self.config.enable_argument_mapping {
672 return None;
673 }
674
675 let sentences: Vec<&str> = query.split(['.', '!', '?']).collect();
676 let query_lower = query.to_lowercase();
677
678 let claim = sentences
680 .iter()
681 .find(|s| {
682 let lower = s.to_lowercase();
683 lower.contains("should")
684 || lower.contains("must")
685 || lower.contains("will")
686 || lower.contains("is the")
687 || lower.contains("are the")
688 })
689 .unwrap_or(sentences.first().unwrap_or(&""))
690 .trim()
691 .to_string();
692
693 let premises: Vec<String> = sentences
695 .iter()
696 .filter(|s| {
697 let lower = s.to_lowercase();
698 lower.contains("because") || lower.contains("since") || lower.contains(" as ")
699 })
700 .map(|s| s.trim().to_string())
701 .collect();
702
703 let evidence: Vec<String> = sentences
705 .iter()
706 .filter(|s| {
707 let lower = s.to_lowercase();
708 lower.contains("data")
709 || lower.contains("study")
710 || lower.contains("research")
711 || lower.contains("evidence")
712 || lower.contains("according to")
713 })
714 .map(|s| s.trim().to_string())
715 .collect();
716
717 let qualifiers: Vec<String> = sentences
719 .iter()
720 .filter(|s| {
721 let lower = s.to_lowercase();
722 lower.contains("unless")
723 || lower.contains("except")
724 || lower.contains("if")
725 || lower.contains("when")
726 || lower.contains("provided")
727 })
728 .map(|s| s.trim().to_string())
729 .collect();
730
731 let rebuttals: Vec<String> = sentences
733 .iter()
734 .filter(|s| {
735 let lower = s.to_lowercase();
736 lower.contains("however")
737 || lower.contains("although")
738 || lower.contains("but")
739 || lower.contains("despite")
740 || lower.contains("critics")
741 })
742 .map(|s| s.trim().to_string())
743 .collect();
744
745 let warrants: Vec<String> = if query_lower.contains("therefore")
747 || query_lower.contains("thus")
748 || query_lower.contains("hence")
749 {
750 vec!["Explicit logical connection present".to_string()]
751 } else if premises.is_empty() {
752 vec!["Implicit warrant: premises assumed to support claim".to_string()]
753 } else {
754 vec![]
755 };
756
757 Some(ArgumentMap {
758 claim,
759 premises,
760 evidence,
761 warrants,
762 qualifiers,
763 rebuttals,
764 })
765 }
766
767 fn calculate_enhanced_confidence(
769 &self,
770 base_confidence: f64,
771 biases: &[CognitiveBias],
772 cultural_assumptions: &[CulturalAssumption],
773 ) -> f64 {
774 let mut confidence = base_confidence;
775
776 for bias in biases {
778 confidence -= bias.confidence * 0.05;
779 }
780
781 for assumption in cultural_assumptions {
783 match assumption.risk_level {
784 FlawSeverity::Critical => confidence -= 0.10,
785 FlawSeverity::Major => confidence -= 0.06,
786 FlawSeverity::Moderate => confidence -= 0.03,
787 FlawSeverity::Minor => confidence -= 0.01,
788 }
789 }
790
791 confidence.clamp(0.0, 0.90) }
793}
794
795impl ThinkToolModule for BrutalHonestyEnhanced {
796 fn config(&self) -> &ThinkToolModuleConfig {
797 &self.module_config
798 }
799
800 fn execute(&self, context: &ThinkToolContext) -> Result<ThinkToolOutput> {
801 if context.query.trim().is_empty() {
803 return Err(Error::validation(
804 "BrutalHonestyEnhanced requires non-empty query input",
805 ));
806 }
807
808 let base_result = self.base_module.execute(context)?;
810 let base_output = &base_result.output;
811
812 let base_analysis = base_output.get("analysis").cloned().unwrap_or(json!({}));
814 let base_verdict = base_output
815 .get("verdict")
816 .cloned()
817 .unwrap_or(json!("indeterminate"));
818 let devils_advocate = base_output.get("devils_advocate").cloned();
819 let critical_fix = base_output.get("critical_fix").cloned();
820
821 let cognitive_biases = self.detect_cognitive_biases(&context.query);
823 let cultural_assumptions = self.detect_cultural_assumptions(&context.query);
824 let steelman = self.generate_steelman(&context.query);
825 let argument_map = self.build_argument_map(&context.query);
826
827 let enhanced_confidence = self.calculate_enhanced_confidence(
829 base_result.confidence,
830 &cognitive_biases,
831 &cultural_assumptions,
832 );
833
834 let enhanced_verdict = if cognitive_biases.len() >= 3 || cultural_assumptions.len() >= 2 {
836 CritiqueVerdict::Weak
837 } else {
838 match base_verdict.as_str() {
840 Some("solid") if !cognitive_biases.is_empty() => CritiqueVerdict::Promising,
841 Some("solid") => CritiqueVerdict::Solid,
842 Some("promising") => CritiqueVerdict::Promising,
843 Some("weak") => CritiqueVerdict::Weak,
844 Some("flawed") => CritiqueVerdict::Flawed,
845 _ => CritiqueVerdict::Indeterminate,
846 }
847 };
848
849 let output = json!({
851 "verdict": enhanced_verdict,
852 "confidence": enhanced_confidence,
853 "base_analysis": base_analysis,
854 "base_verdict": base_verdict,
855 "enhanced_analysis": {
856 "cognitive_biases": cognitive_biases,
857 "bias_count": cognitive_biases.len(),
858 "cultural_assumptions": cultural_assumptions,
859 "cultural_assumption_count": cultural_assumptions.len(),
860 "steelman": steelman,
861 "argument_map": argument_map,
862 },
863 "devils_advocate": devils_advocate,
864 "critical_fix": critical_fix,
865 "metadata": {
866 "input_length": context.query.len(),
867 "previous_steps_count": context.previous_steps.len(),
868 "analysis_depth": format!("{:?}", self.config.cognitive_bias_depth),
869 "cultural_analysis_enabled": self.config.enable_cultural_analysis,
870 "steelmanning_enabled": self.config.enable_steelmanning,
871 }
872 });
873
874 Ok(ThinkToolOutput {
875 module: self.module_config.name.clone(),
876 confidence: enhanced_confidence,
877 output,
878 })
879 }
880}
881
882#[cfg(test)]
883mod tests {
884 use super::*;
885
886 #[test]
887 fn test_default_enhanced_module() {
888 let module = BrutalHonestyEnhanced::new();
889 assert_eq!(module.config().name, "BrutalHonestyEnhanced");
890 assert_eq!(module.config().version, "3.0.0");
891 assert!(module.enhanced_config().enable_cultural_analysis);
892 assert!(module.enhanced_config().enable_steelmanning);
893 }
894
895 #[test]
896 fn test_builder_pattern() {
897 let module = BrutalHonestyEnhanced::builder()
898 .severity(CritiqueSeverity::Ruthless)
899 .enable_cultural_analysis(false)
900 .cognitive_bias_depth(CognitiveBiasDepth::Deep)
901 .build();
902
903 assert_eq!(
904 module.enhanced_config().base_config.severity,
905 CritiqueSeverity::Ruthless
906 );
907 assert!(!module.enhanced_config().enable_cultural_analysis);
908 assert_eq!(
909 module.enhanced_config().cognitive_bias_depth,
910 CognitiveBiasDepth::Deep
911 );
912 }
913
914 #[test]
915 fn test_cognitive_bias_detection() {
916 let module = BrutalHonestyEnhanced::new();
917 let biases = module.detect_cognitive_biases(
918 "I'm certain this will succeed. We've already invested too much to stop.",
919 );
920
921 assert!(!biases.is_empty());
922
923 let has_overconfidence = biases.iter().any(|b| b.name == "Overconfidence Bias");
925 assert!(has_overconfidence);
926
927 let has_sunk_cost = biases.iter().any(|b| b.name == "Sunk Cost Fallacy");
929 assert!(has_sunk_cost);
930 }
931
932 #[test]
933 fn test_cultural_assumption_detection() {
934 let module = BrutalHonestyEnhanced::new();
935 let assumptions = module.detect_cultural_assumptions(
936 "We need direct communication and individual achievement focus.",
937 );
938
939 assert!(!assumptions.is_empty());
940 }
941
942 #[test]
943 fn test_steelman_generation() {
944 let module = BrutalHonestyEnhanced::new();
945 let steelman = module.generate_steelman("We should expand immediately");
946
947 assert!(steelman.is_some());
948 let s = steelman.unwrap();
949 assert!(!s.argument.is_empty());
950 assert!(!s.premises.is_empty());
951 }
952
953 #[test]
954 fn test_argument_mapping() {
955 let module = BrutalHonestyEnhanced::new();
956 let map = module.build_argument_map(
957 "We should launch now because the market is ready. Data shows strong demand. However, there are risks.",
958 );
959
960 assert!(map.is_some());
961 let m = map.unwrap();
962 assert!(!m.claim.is_empty());
963 }
964
965 #[test]
966 fn test_execute_valid_input() {
967 let module = BrutalHonestyEnhanced::new();
968 let context = ThinkToolContext {
969 query:
970 "We're certain this will succeed because everyone agrees it's the best approach."
971 .to_string(),
972 previous_steps: vec![],
973 };
974
975 let result = module.execute(&context).unwrap();
976 assert_eq!(result.module, "BrutalHonestyEnhanced");
977 assert!(result.confidence > 0.0);
978 assert!(result.confidence <= 0.90);
979
980 let output = &result.output;
982 assert!(output.get("verdict").is_some());
983 assert!(output.get("base_analysis").is_some());
984 assert!(output.get("enhanced_analysis").is_some());
985 }
986
987 #[test]
988 fn test_execute_empty_input() {
989 let module = BrutalHonestyEnhanced::new();
990 let context = ThinkToolContext {
991 query: "".to_string(),
992 previous_steps: vec![],
993 };
994
995 let result = module.execute(&context);
996 assert!(result.is_err());
997 }
998
999 #[test]
1000 fn test_bias_depth_affects_detection() {
1001 let basic = BrutalHonestyEnhanced::builder()
1002 .cognitive_bias_depth(CognitiveBiasDepth::Basic)
1003 .build();
1004 let deep = BrutalHonestyEnhanced::builder()
1005 .cognitive_bias_depth(CognitiveBiasDepth::Deep)
1006 .build();
1007
1008 let query =
1009 "I'm certain this simple plan with a clear timeline will succeed. Everyone agrees.";
1010 let basic_biases = basic.detect_cognitive_biases(query);
1011 let deep_biases = deep.detect_cognitive_biases(query);
1012
1013 assert!(deep_biases.len() >= basic_biases.len());
1015 }
1016
1017 #[test]
1018 fn test_enhanced_confidence_reduction() {
1019 let module = BrutalHonestyEnhanced::new();
1020
1021 let biases = vec![CognitiveBias {
1023 name: "Test Bias".to_string(),
1024 category: BiasCategory::DecisionMaking,
1025 manifestation: "Test".to_string(),
1026 confidence: 0.8,
1027 debiasing_strategy: "Test".to_string(),
1028 }];
1029 let assumptions = vec![CulturalAssumption {
1030 assumption: "Test".to_string(),
1031 context: "Test".to_string(),
1032 exceptions: vec![],
1033 risk_level: FlawSeverity::Major,
1034 }];
1035
1036 let confidence = module.calculate_enhanced_confidence(0.70, &biases, &assumptions);
1037 assert!(confidence < 0.70);
1038 }
1039}