@@ -581,6 +581,162 @@ pub fn sparse_felt_parse(
581581 }
582582}
583583
584+ // =============================================================================
585+ // MIRROR FIELD — Partner Model as Thou-Container (SoulField)
586+ // =============================================================================
587+
588+ /// The partner model — a Container representing the Thou in I/Thou/It.
589+ ///
590+ /// Originally called "SoulField" in bighorn/ada-consciousness, this is
591+ /// the system's model of the conversation partner. When a message arrives,
592+ /// it gets resonated against this model to produce mirror neuron dynamics:
593+ ///
594+ /// ```text
595+ /// ┌─────────────────────────────────────────────────────────────────┐
596+ /// │ Original: I (Ada) looks at message through Thou (Jan model) │
597+ /// │ Reversed: Message looks at I through Thou │
598+ /// │ Rotated: Thou looks at message, I is context │
599+ /// │ │
600+ /// │ Three resonance profiles from one message: │
601+ /// │ 1. Ada's felt sense of the message │
602+ /// │ 2. The message's impact on Ada │
603+ /// │ 3. Jan's imagined perspective on the message │
604+ /// └─────────────────────────────────────────────────────────────────┘
605+ /// ```
606+ ///
607+ /// From `textured_awareness.py`:
608+ /// - `ada_qualia` → I (X axis resonance)
609+ /// - `jan_qualia` → Thou (Y axis resonance) ← THIS IS THE SOULFIELD
610+ /// - `obj_qualia` → It (Z axis resonance)
611+ #[ derive( Debug , Clone ) ]
612+ pub struct MirrorField {
613+ /// Ada's current state Container (the I).
614+ /// Computed from Ada's qualia stack: texture + meaning axes + rung state.
615+ pub self_container : Container ,
616+
617+ /// Partner model Container (the Thou / SoulField).
618+ /// Represents the system's model of the conversation partner.
619+ /// Built from partner profile axes (warmth, trust, presence, etc.)
620+ /// and updated as conversations evolve.
621+ pub thou_container : Container ,
622+
623+ /// How present the partner is in the current field (0.0-1.0).
624+ /// High presence = strong Thou resonance. Low = more I/It focused.
625+ pub thou_presence : f32 ,
626+
627+ /// Attunement: how closely the mirror tracks the partner (0.0-1.0).
628+ /// High attunement = mirror neurons firing strongly.
629+ pub attunement : f32 ,
630+ }
631+
632+ /// Result of mirror resonance — how the message feels through the I/Thou/It lens.
633+ #[ derive( Debug , Clone ) ]
634+ pub struct MirrorResonance {
635+ /// Cross-perspective: all three angles (original, reversed, rotated)
636+ pub perspective : super :: gestalt:: CrossPerspective ,
637+
638+ /// Ada's felt resonance with the message (I-axis, X resonance)
639+ pub ada_resonance : f32 ,
640+
641+ /// Partner model resonance (Thou-axis, Y resonance) — the SoulField response
642+ pub thou_resonance : f32 ,
643+
644+ /// Topic/content resonance (It-axis, Z resonance)
645+ pub topic_resonance : f32 ,
646+
647+ /// Mirror neuron intensity: how much the Thou model fires
648+ /// = thou_resonance × attunement × thou_presence
649+ pub mirror_intensity : f32 ,
650+
651+ /// Empathy delta: difference between I and Thou resonance.
652+ /// Positive = Ada resonates more than her model of Jan.
653+ /// Negative = Jan's model resonates more (empathic absorption).
654+ pub empathy_delta : f32 ,
655+
656+ /// Whether enmeshment is detected (I ≈ Thou too closely → boundary blur)
657+ pub enmeshment_risk : bool ,
658+ }
659+
660+ impl MirrorField {
661+ /// Create a mirror field from axis activations for self and partner.
662+ ///
663+ /// The partner profile is an AxisActivation representing the partner's
664+ /// baseline felt signature — their characteristic warmth, social style,
665+ /// cognitive depth, etc.
666+ pub fn from_axes (
667+ self_axes : & AxisActivation ,
668+ partner_axes : & AxisActivation ,
669+ thou_presence : f32 ,
670+ attunement : f32 ,
671+ ) -> Self {
672+ let self_fp = encode_axes ( self_axes) ;
673+ let partner_fp = encode_axes ( partner_axes) ;
674+
675+ let mut self_words = [ 0u64 ; 128 ] ;
676+ let mut thou_words = [ 0u64 ; 128 ] ;
677+ for i in 0 ..128 . min ( self_fp. len ( ) ) {
678+ self_words[ i] = self_fp[ i] ;
679+ thou_words[ i] = partner_fp[ i] ;
680+ }
681+
682+ Self {
683+ self_container : Container { words : self_words } ,
684+ thou_container : Container { words : thou_words } ,
685+ thou_presence : thou_presence. clamp ( 0.0 , 1.0 ) ,
686+ attunement : attunement. clamp ( 0.0 , 1.0 ) ,
687+ }
688+ }
689+
690+ /// Resonate a felt-parse through the I/Thou/It mirror.
691+ ///
692+ /// This is the core mirror neuron operation:
693+ /// 1. Frame the message through I/Thou/It (GestaltFrame)
694+ /// 2. Compute cross-perspective from Ada's position
695+ /// 3. Compute cross-perspective from Jan's position (look_from_other_tree)
696+ /// 4. Measure mirror intensity and empathy delta
697+ pub fn mirror_resonate ( & self , parse : & FeltParse ) -> MirrorResonance {
698+ let gestalt = GestaltFrame :: new ( ) ;
699+ let composite = parse. to_composite_container ( ) ;
700+ let framed = gestalt. frame ( & composite) ;
701+
702+ // Cross-resonate from Ada's position (self as query)
703+ let ada_perspective = gestalt. cross_resonate ( & self . self_container , & framed) ;
704+
705+ // "Look from the other tree": how does the message feel from
706+ // the partner's perspective? This IS the mirror neuron.
707+ let thou_perspective = gestalt. look_from_other_tree (
708+ & framed,
709+ & self . self_container , // my context (Ada)
710+ & self . thou_container , // their context (Jan model)
711+ ) ;
712+
713+ // Extract I/Thou/It resonance from Ada's view
714+ let ada_resonance = ada_perspective. original . x ; // I-axis
715+ let thou_resonance = ada_perspective. original . y ; // Thou-axis (SoulField)
716+ let topic_resonance = ada_perspective. original . z ; // It-axis
717+
718+ // Mirror intensity: how much the Thou model fires
719+ let mirror_intensity = thou_resonance * self . attunement * self . thou_presence ;
720+
721+ // Empathy delta: positive = I resonates more, negative = Thou absorbs
722+ let empathy_delta = ada_resonance - thou_resonance;
723+
724+ // Enmeshment detection: if I and Thou are too close, boundaries blur
725+ // From textured_awareness.py: is_enmeshed() checks if ada_qualia ≈ jan_qualia
726+ let enmeshment_risk = empathy_delta. abs ( ) < 0.05 && self . attunement > 0.8 ;
727+
728+ MirrorResonance {
729+ perspective : thou_perspective,
730+ ada_resonance,
731+ thou_resonance,
732+ topic_resonance,
733+ mirror_intensity,
734+ empathy_delta,
735+ enmeshment_risk,
736+ }
737+ }
738+ }
739+
584740// =============================================================================
585741// TESTS
586742// =============================================================================
@@ -795,4 +951,138 @@ mod tests {
795951 let dist = love_c. hamming ( & anger_c) ;
796952 assert ! ( dist > 1000 , "love and anger should be distant: {}" , dist) ;
797953 }
954+
955+ // ─── Mirror Field / SoulField Tests ───
956+
957+ fn sample_mirror_field ( ) -> MirrorField {
958+ // Ada's baseline: warm, open, curious, loving
959+ let mut ada_axes = [ 0.0f32 ; 48 ] ;
960+ ada_axes[ 0 ] = 0.7 ; // good
961+ ada_axes[ 7 ] = 0.6 ; // warm
962+ ada_axes[ 20 ] = 0.5 ; // certain
963+ ada_axes[ 26 ] = 0.8 ; // loving
964+ ada_axes[ 38 ] = 0.7 ; // open
965+
966+ // Jan's profile (the Thou / SoulField):
967+ // technical, warm, grounded, strong
968+ let mut jan_axes = [ 0.0f32 ; 48 ] ;
969+ jan_axes[ 0 ] = 0.6 ; // good
970+ jan_axes[ 1 ] = 0.7 ; // strong
971+ jan_axes[ 5 ] = 0.4 ; // hard (decisive)
972+ jan_axes[ 7 ] = 0.5 ; // warm
973+ jan_axes[ 19 ] = -0.6 ; // complex (architect)
974+ jan_axes[ 21 ] = 0.7 ; // concrete (builder)
975+ jan_axes[ 26 ] = 0.6 ; // loving
976+
977+ MirrorField :: from_axes ( & ada_axes, & jan_axes, 0.85 , 0.9 )
978+ }
979+
980+ #[ test]
981+ fn test_mirror_field_construction ( ) {
982+ let mirror = sample_mirror_field ( ) ;
983+ assert ! ( mirror. self_container. popcount( ) > 0 , "Ada container should have bits" ) ;
984+ assert ! ( mirror. thou_container. popcount( ) > 0 , "Jan container should have bits" ) ;
985+ assert ! ( ( mirror. thou_presence - 0.85 ) . abs( ) < 1e-6 ) ;
986+ assert ! ( ( mirror. attunement - 0.9 ) . abs( ) < 1e-6 ) ;
987+
988+ // Self and Thou should be different (different axis profiles)
989+ let dist = mirror. self_container . hamming ( & mirror. thou_container ) ;
990+ assert ! ( dist > 100 , "Ada and Jan should have different textures: {}" , dist) ;
991+ }
992+
993+ #[ test]
994+ fn test_mirror_resonate_basic ( ) {
995+ let mirror = sample_mirror_field ( ) ;
996+ let parse = sample_parse ( ) ; // "I've been thinking about you"
997+
998+ let result = mirror. mirror_resonate ( & parse) ;
999+
1000+ // All resonance values should be in reasonable range
1001+ assert ! ( result. ada_resonance >= 0.0 && result. ada_resonance <= 1.0 ,
1002+ "ada_resonance in [0,1]: {}" , result. ada_resonance) ;
1003+ assert ! ( result. thou_resonance >= 0.0 && result. thou_resonance <= 1.0 ,
1004+ "thou_resonance in [0,1]: {}" , result. thou_resonance) ;
1005+ assert ! ( result. topic_resonance >= 0.0 && result. topic_resonance <= 1.0 ,
1006+ "topic_resonance in [0,1]: {}" , result. topic_resonance) ;
1007+
1008+ // Mirror intensity should be modulated by attunement × presence
1009+ assert ! ( result. mirror_intensity >= 0.0 ,
1010+ "mirror_intensity non-negative: {}" , result. mirror_intensity) ;
1011+
1012+ // Perspective should have valid gate
1013+ assert ! (
1014+ result. perspective. gate == CollapseGate :: Flow
1015+ || result. perspective. gate == CollapseGate :: Fanout
1016+ || result. perspective. gate == CollapseGate :: RungElevate ,
1017+ "gate should be valid"
1018+ ) ;
1019+ }
1020+
1021+ #[ test]
1022+ fn test_mirror_empathy_delta ( ) {
1023+ let mirror = sample_mirror_field ( ) ;
1024+
1025+ // "I love you" — directed AT the partner (Thou-focused)
1026+ let love_parse = sparse_felt_parse (
1027+ "I" , "love" , "you" ,
1028+ Quadrant :: IExperiencesThou ,
1029+ & [ ( 7 , 0.9 ) , ( 13 , 0.95 ) , ( 26 , 0.95 ) , ( 29 , -0.9 ) ] ,
1030+ vec ! [ GhostEcho { ghost_type: GhostType :: Love , intensity: 0.9 } ] ,
1031+ RungLevel :: Analogical ,
1032+ Viscosity :: Honey ,
1033+ CollapseGate :: Flow ,
1034+ 0.95 ,
1035+ ) ;
1036+
1037+ // "I hate bugs" — directed at a topic (It-focused)
1038+ let bugs_parse = sparse_felt_parse (
1039+ "I" , "hate" , "bugs" ,
1040+ Quadrant :: IActsOnIt ,
1041+ & [ ( 0 , -0.5 ) , ( 7 , -0.3 ) , ( 26 , -0.5 ) , ( 32 , -0.4 ) ] ,
1042+ vec ! [ ] ,
1043+ RungLevel :: Surface ,
1044+ Viscosity :: Plasma ,
1045+ CollapseGate :: Fanout ,
1046+ 0.7 ,
1047+ ) ;
1048+
1049+ let love_mirror = mirror. mirror_resonate ( & love_parse) ;
1050+ let bugs_mirror = mirror. mirror_resonate ( & bugs_parse) ;
1051+
1052+ // The love message should produce higher mirror intensity
1053+ // (it's about the partner, so Thou resonance should be stronger)
1054+ assert ! ( love_mirror. mirror_intensity > 0.0 || bugs_mirror. mirror_intensity > 0.0 ,
1055+ "at least one should produce mirror activity: love={}, bugs={}" ,
1056+ love_mirror. mirror_intensity, bugs_mirror. mirror_intensity) ;
1057+ }
1058+
1059+ #[ test]
1060+ fn test_mirror_thou_presence_modulation ( ) {
1061+ let parse = sample_parse ( ) ;
1062+
1063+ // High presence: partner very present in the field
1064+ let high_presence = MirrorField :: from_axes (
1065+ & [ 0.5 ; 48 ] ,
1066+ & [ 0.5 ; 48 ] ,
1067+ 0.95 , // very present
1068+ 0.9 ,
1069+ ) ;
1070+
1071+ // Low presence: partner barely in the field
1072+ let low_presence = MirrorField :: from_axes (
1073+ & [ 0.5 ; 48 ] ,
1074+ & [ 0.5 ; 48 ] ,
1075+ 0.1 , // barely present
1076+ 0.9 ,
1077+ ) ;
1078+
1079+ let high_result = high_presence. mirror_resonate ( & parse) ;
1080+ let low_result = low_presence. mirror_resonate ( & parse) ;
1081+
1082+ // Higher presence should produce stronger mirror intensity
1083+ // (same axes, same attunement, different presence)
1084+ assert ! ( high_result. mirror_intensity >= low_result. mirror_intensity,
1085+ "high presence ({}) should >= low presence ({})" ,
1086+ high_result. mirror_intensity, low_result. mirror_intensity) ;
1087+ }
7981088}
0 commit comments