@@ -694,6 +694,8 @@ class _SequenceAutomaton:
694694 "_detected_pattern" ,
695695 "_bifurcation_context" ,
696696 "_destabilizer_context" ,
697+ "_thol_stack" ,
698+ "_thol_subsequences" ,
697699 )
698700
699701 def __init__ (self ) -> None :
@@ -716,6 +718,9 @@ def __init__(self) -> None:
716718 "moderate" : deque (maxlen = BIFURCATION_WINDOWS ["moderate" ]),
717719 "weak" : deque (maxlen = BIFURCATION_WINDOWS ["weak" ]),
718720 }
721+ # THOL recursive validation: Track nested THOL blocks and their subsequences
722+ self ._thol_stack : list [int ] = [] # Stack of THOL opening indices
723+ self ._thol_subsequences : dict [int , list [str ]] = {} # Subsequences by opening index
719724
720725 def run (self , names : Sequence [str ]) -> None :
721726 if not names :
@@ -788,11 +793,35 @@ def _consume(self, token: str, index: int) -> None:
788793 if canonical == DISSONANCE :
789794 self ._found_dissonance = True
790795
791- # Track THOL state
796+ # Track THOL state with recursive support
792797 if canonical == SELF_ORGANIZATION :
798+ # THOL opening: push to stack and initialize subsequence
799+ self ._thol_stack .append (index )
800+ self ._thol_subsequences [index ] = []
793801 self ._open_thol = True
794802 elif self ._open_thol and canonical in SELF_ORGANIZATION_CLOSURES :
795- self ._open_thol = False
803+ # THOL closure: validate subsequence recursively
804+ if not self ._thol_stack :
805+ raise SequenceSyntaxError (
806+ index = index ,
807+ token = token ,
808+ message = f"{ operator_display_name (canonical )} closure without corresponding { operator_display_name (SELF_ORGANIZATION )} opening"
809+ )
810+
811+ thol_start = self ._thol_stack .pop ()
812+ thol_subseq = self ._thol_subsequences [thol_start ]
813+
814+ # Validate THOL subsequence recursively
815+ self ._validate_thol_subsequence (thol_subseq , thol_start , index , token )
816+
817+ # Update _open_thol based on stack state (supports nested THOL)
818+ self ._open_thol = bool (self ._thol_stack )
819+ elif self ._open_thol and self ._thol_stack :
820+ # Track operators within THOL block (excluding nested THOL openings)
821+ # Nested THOL openings are handled separately, closures are part of inner subsequence
822+ if canonical != SELF_ORGANIZATION :
823+ current_thol = self ._thol_stack [- 1 ]
824+ self ._thol_subsequences [current_thol ].append (canonical )
796825
797826 # Validate sequential compatibility if not first token
798827 # Only validate if both prev and current are known operators
@@ -999,6 +1028,76 @@ def _has_graduated_destabilizer(self, current_index: int) -> bool:
9991028
10001029 return False
10011030
1031+ def _validate_thol_subsequence (
1032+ self ,
1033+ subsequence : list [str ],
1034+ start_index : int ,
1035+ end_index : int ,
1036+ end_token : str
1037+ ) -> None :
1038+ """Validate recursively the subsequence within THOL block.
1039+
1040+ TNFR Fractal Principle: THOL (self-organization) is a fractal operator
1041+ that encapsulates autonomous reorganization. The subsequence must:
1042+ 1. Be non-empty (THOL without content is meaningless)
1043+ 2. Respect all grammar rules R1-R6 (recursive coherence)
1044+
1045+ Parameters
1046+ ----------
1047+ subsequence : list[str]
1048+ Operators within THOL block (between opening and closure)
1049+ start_index : int
1050+ Index of THOL opening in parent sequence
1051+ end_index : int
1052+ Index of THOL closure in parent sequence
1053+ end_token : str
1054+ Token used for closure (for error messages)
1055+
1056+ Raises
1057+ ------
1058+ SequenceSyntaxError
1059+ If subsequence is empty or invalid
1060+
1061+ Notes
1062+ -----
1063+ From TNFR Manual §3.2.2 (Ontología fractal resonante):
1064+ "Los NFRs pueden anidarse jerárquicamente: un nodo puede contener
1065+ nodos internos coherentes, dando lugar a una estructura fractal."
1066+
1067+ This validation ensures THOL maintains operational fractality:
1068+ structures are coherent at all scales.
1069+
1070+ Autonomy is implicit: if the subsequence passes all grammar rules,
1071+ it is by definition autonomous (can function independently).
1072+ """
1073+ # Validation 1: Non-empty subsequence
1074+ if not subsequence :
1075+ raise SequenceSyntaxError (
1076+ index = end_index ,
1077+ token = end_token ,
1078+ message = (
1079+ f"{ operator_display_name (SELF_ORGANIZATION )} block is empty "
1080+ f"(opened at position { start_index } ). Subsequence must contain "
1081+ f"at least one operator for autonomous reorganization."
1082+ )
1083+ )
1084+
1085+ # Validation 2: Recursive grammar validation
1086+ # Create new automaton to validate subsequence independently
1087+ try :
1088+ nested_automaton = _SequenceAutomaton ()
1089+ nested_automaton .run (subsequence )
1090+ except SequenceSyntaxError as e :
1091+ # Re-raise with THOL context
1092+ raise SequenceSyntaxError (
1093+ index = start_index + e .index + 1 , # Offset by THOL position
1094+ token = e .token ,
1095+ message = (
1096+ f"Invalid subsequence within { operator_display_name (SELF_ORGANIZATION )} "
1097+ f"block (opened at position { start_index } ): { e .message } "
1098+ )
1099+ ) from e
1100+
10021101 def _validate_threshold_physics (self , sequence : Sequence [str ]) -> None :
10031102 """C4: Validate threshold physics - transformations require context.
10041103
0 commit comments