reasonkit/thinktool/modules/
brutalhonesty_enhanced.rs

1//! Enhanced BrutalHonesty Module with Extended Analysis
2//!
3//! Advanced adversarial self-critique that builds upon the base BrutalHonesty
4//! module with additional analysis capabilities:
5//!
6//! - **Multi-Cultural Bias Detection**: Identifies cultural assumptions
7//! - **Cognitive Bias Catalog**: Checks for common cognitive biases
8//! - **Argument Mapping**: Structures the argument for analysis
9//! - **Steelmanning**: Constructs the strongest version of opposing arguments
10//!
11//! ## Design Philosophy
12//!
13//! The enhanced module provides deeper analysis for high-stakes decisions
14//! where thorough adversarial review is critical. It is designed to be
15//! used in conjunction with or as a replacement for the base module.
16//!
17//! ## Usage
18//!
19//! ```ignore
20//! use reasonkit::thinktool::modules::{BrutalHonestyEnhanced, ThinkToolContext};
21//!
22//! let module = BrutalHonestyEnhanced::builder()
23//!     .enable_cultural_analysis(true)
24//!     .enable_steelmanning(true)
25//!     .cognitive_bias_depth(CognitiveBiasDepth::Deep)
26//!     .build();
27//!
28//! let context = ThinkToolContext {
29//!     query: "We should expand to international markets immediately".to_string(),
30//!     previous_steps: vec![],
31//! };
32//!
33//! let result = module.execute(&context)?;
34//! ```
35
36use 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/// Depth of cognitive bias analysis.
45#[derive(Debug, Clone, Copy, PartialEq, Eq, Default, Serialize, Deserialize)]
46#[serde(rename_all = "snake_case")]
47pub enum CognitiveBiasDepth {
48    /// Basic bias detection (5 most common biases)
49    Basic,
50    /// Standard bias detection (15 common biases)
51    #[default]
52    Standard,
53    /// Deep bias detection (30+ biases with sub-categories)
54    Deep,
55}
56
57/// A detected cognitive bias.
58#[derive(Debug, Clone, Serialize, Deserialize)]
59pub struct CognitiveBias {
60    /// Name of the bias
61    pub name: String,
62    /// Category of the bias
63    pub category: BiasCategory,
64    /// Description of how this bias manifests
65    pub manifestation: String,
66    /// Confidence that this bias is present (0.0-1.0)
67    pub confidence: f64,
68    /// Debiasing strategy
69    pub debiasing_strategy: String,
70}
71
72/// Category of cognitive bias.
73#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
74#[serde(rename_all = "snake_case")]
75pub enum BiasCategory {
76    /// Decision-making biases (anchoring, availability, etc.)
77    DecisionMaking,
78    /// Social biases (in-group, halo effect, etc.)
79    Social,
80    /// Memory biases (hindsight, rosy retrospection, etc.)
81    Memory,
82    /// Probability biases (gambler's fallacy, base rate neglect, etc.)
83    Probability,
84    /// Self-related biases (overconfidence, self-serving, etc.)
85    SelfRelated,
86}
87
88/// A cultural assumption detected in reasoning.
89#[derive(Debug, Clone, Serialize, Deserialize)]
90pub struct CulturalAssumption {
91    /// The assumption
92    pub assumption: String,
93    /// Cultural context where this assumption holds
94    pub context: String,
95    /// Cultures where this assumption may not hold
96    pub exceptions: Vec<String>,
97    /// Risk of misapplication
98    pub risk_level: FlawSeverity,
99}
100
101/// A steelmanned counter-argument.
102#[derive(Debug, Clone, Serialize, Deserialize)]
103pub struct SteelmanArgument {
104    /// The strongest version of the opposing argument
105    pub argument: String,
106    /// Key premises of the steelmanned argument
107    pub premises: Vec<String>,
108    /// How this argument challenges the original position
109    pub challenge: String,
110    /// Strength of the steelmanned argument (0.0-1.0)
111    pub strength: f64,
112}
113
114/// Structured argument map.
115#[derive(Debug, Clone, Serialize, Deserialize)]
116pub struct ArgumentMap {
117    /// Main claim being analyzed
118    pub claim: String,
119    /// Supporting premises
120    pub premises: Vec<String>,
121    /// Evidence cited
122    pub evidence: Vec<String>,
123    /// Warrants (logical connections)
124    pub warrants: Vec<String>,
125    /// Qualifiers (conditions or limitations)
126    pub qualifiers: Vec<String>,
127    /// Rebuttals (counter-arguments acknowledged)
128    pub rebuttals: Vec<String>,
129}
130
131/// Configuration for the enhanced BrutalHonesty module.
132#[derive(Debug, Clone, Serialize, Deserialize)]
133pub struct EnhancedConfig {
134    /// Base BrutalHonesty configuration
135    pub base_config: BrutalHonestyConfig,
136    /// Enable cultural assumption analysis
137    pub enable_cultural_analysis: bool,
138    /// Enable steelmanning of counter-arguments
139    pub enable_steelmanning: bool,
140    /// Enable argument mapping
141    pub enable_argument_mapping: bool,
142    /// Depth of cognitive bias analysis
143    pub cognitive_bias_depth: CognitiveBiasDepth,
144    /// Target cultures for cultural analysis
145    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/// Builder for enhanced BrutalHonesty module.
168#[derive(Debug, Default)]
169pub struct EnhancedBuilder {
170    config: EnhancedConfig,
171}
172
173impl EnhancedBuilder {
174    /// Create a new builder with default configuration.
175    pub fn new() -> Self {
176        Self::default()
177    }
178
179    /// Set the base critique severity.
180    pub fn severity(mut self, severity: CritiqueSeverity) -> Self {
181        self.config.base_config.severity = severity;
182        self
183    }
184
185    /// Enable or disable cultural analysis.
186    pub fn enable_cultural_analysis(mut self, enable: bool) -> Self {
187        self.config.enable_cultural_analysis = enable;
188        self
189    }
190
191    /// Enable or disable steelmanning.
192    pub fn enable_steelmanning(mut self, enable: bool) -> Self {
193        self.config.enable_steelmanning = enable;
194        self
195    }
196
197    /// Enable or disable argument mapping.
198    pub fn enable_argument_mapping(mut self, enable: bool) -> Self {
199        self.config.enable_argument_mapping = enable;
200        self
201    }
202
203    /// Set cognitive bias analysis depth.
204    pub fn cognitive_bias_depth(mut self, depth: CognitiveBiasDepth) -> Self {
205        self.config.cognitive_bias_depth = depth;
206        self
207    }
208
209    /// Set target cultures for cultural analysis.
210    pub fn target_cultures(mut self, cultures: Vec<String>) -> Self {
211        self.config.target_cultures = cultures;
212        self
213    }
214
215    /// Build the enhanced BrutalHonesty module.
216    pub fn build(self) -> BrutalHonestyEnhanced {
217        BrutalHonestyEnhanced::with_config(self.config)
218    }
219}
220
221/// Enhanced BrutalHonesty module with extended analysis.
222pub struct BrutalHonestyEnhanced {
223    /// Module base configuration
224    module_config: ThinkToolModuleConfig,
225    /// Enhanced configuration
226    config: EnhancedConfig,
227    /// Base BrutalHonesty module for core analysis
228    base_module: BrutalHonesty,
229}
230
231impl Default for BrutalHonestyEnhanced {
232    fn default() -> Self {
233        Self::new()
234    }
235}
236
237impl BrutalHonestyEnhanced {
238    /// Create a new enhanced BrutalHonesty module with default configuration.
239    pub fn new() -> Self {
240        Self::with_config(EnhancedConfig::default())
241    }
242
243    /// Create an enhanced module with custom configuration.
244    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, // Slightly higher weight due to deeper analysis
255            },
256            config,
257            base_module,
258        }
259    }
260
261    /// Create a builder for customizing the module.
262    pub fn builder() -> EnhancedBuilder {
263        EnhancedBuilder::new()
264    }
265
266    /// Get the current configuration.
267    pub fn enhanced_config(&self) -> &EnhancedConfig {
268        &self.config
269    }
270
271    /// Detect cognitive biases in the reasoning.
272    fn detect_cognitive_biases(&self, query: &str) -> Vec<CognitiveBias> {
273        let mut biases = Vec::new();
274        let query_lower = query.to_lowercase();
275
276        // Define bias patterns based on depth
277        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    /// Get basic (most common) cognitive biases.
297    fn get_basic_biases(&self, query: &str) -> Vec<CognitiveBias> {
298        let mut biases = Vec::new();
299
300        // Confirmation bias
301        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        // Anchoring bias
318        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        // Availability heuristic
331        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        // Overconfidence
345        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        // Sunk cost fallacy
357        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    /// Get standard cognitive biases.
376    fn get_standard_biases(&self, query: &str) -> Vec<CognitiveBias> {
377        let mut biases = Vec::new();
378
379        // Hindsight bias
380        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        // Bandwagon effect
396        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        // Status quo bias
408        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        // Fundamental attribution error
421        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        // Optimism bias
434        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        // Framing effect
449        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    /// Get deep cognitive biases.
464    fn get_deep_biases(&self, query: &str) -> Vec<CognitiveBias> {
465        let mut biases = Vec::new();
466
467        // Planning fallacy
468        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        // Survivorship bias
479        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        // Narrative fallacy
494        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        // Dunning-Kruger effect
505        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        // Affect heuristic
518        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    /// Detect cultural assumptions in the reasoning.
533    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        // Individualism vs Collectivism
542        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        // Direct communication assumption
559        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        // Time orientation
576        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        // Hierarchical assumptions
592        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    /// Generate a steelmanned counter-argument.
611    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        // Generate contextual steelman arguments
619        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    /// Build an argument map from the input.
670    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        // Extract claim (usually first substantial sentence or sentence with "should/must/will")
679        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        // Extract premises (sentences with "because", "since", "as")
694        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        // Extract evidence references
704        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        // Extract qualifiers
718        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        // Extract rebuttals (acknowledged counter-arguments)
732        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        // Extract warrants (logical connections)
746        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    /// Calculate enhanced confidence incorporating all analyses.
768    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        // Apply cognitive bias penalties
777        for bias in biases {
778            confidence -= bias.confidence * 0.05;
779        }
780
781        // Apply cultural assumption penalties
782        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) // Slightly lower max than base due to deeper skepticism
792    }
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        // Validate input
802        if context.query.trim().is_empty() {
803            return Err(Error::validation(
804                "BrutalHonestyEnhanced requires non-empty query input",
805            ));
806        }
807
808        // Run base BrutalHonesty analysis
809        let base_result = self.base_module.execute(context)?;
810        let base_output = &base_result.output;
811
812        // Extract base analysis results
813        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        // Perform enhanced analyses
822        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        // Calculate enhanced confidence
828        let enhanced_confidence = self.calculate_enhanced_confidence(
829            base_result.confidence,
830            &cognitive_biases,
831            &cultural_assumptions,
832        );
833
834        // Determine enhanced verdict
835        let enhanced_verdict = if cognitive_biases.len() >= 3 || cultural_assumptions.len() >= 2 {
836            CritiqueVerdict::Weak
837        } else {
838            // Parse base verdict and potentially downgrade
839            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        // Build output
850        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        // Should detect overconfidence
924        let has_overconfidence = biases.iter().any(|b| b.name == "Overconfidence Bias");
925        assert!(has_overconfidence);
926
927        // Should detect sunk cost fallacy
928        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        // Check output structure
981        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        // Deep analysis should find more biases
1014        assert!(deep_biases.len() >= basic_biases.len());
1015    }
1016
1017    #[test]
1018    fn test_enhanced_confidence_reduction() {
1019        let module = BrutalHonestyEnhanced::new();
1020
1021        // With biases and cultural assumptions, confidence should be lower
1022        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}