@@ -917,13 +917,15 @@ def _validate_transition(
917917 )
918918
919919 # R5: Validate structural frequency transitions using ONLY canonical TNFR physics
920- freq_valid , freq_msg = validate_frequency_transition (prev , curr )
921- if not freq_valid :
922- # Invalid frequency transition violates TNFR structural dynamics
923- raise SequenceSyntaxError (
924- index = index ,
925- token = token ,
926- message = f"{ operator_display_name (curr )} invalid after { operator_display_name (prev )} : { freq_msg } " ,
920+ # Frequency transitions now generate warnings for suboptimal patterns, not errors
921+ freq_valid , freq_msg , freq_is_warning = validate_frequency_transition (prev , curr )
922+ if freq_is_warning :
923+ # Suboptimal frequency transition - warn but allow
924+ import warnings
925+ warnings .warn (
926+ f"{ operator_display_name (curr )} after { operator_display_name (prev )} : { freq_msg } " ,
927+ UserWarning ,
928+ stacklevel = 3
927929 )
928930
929931 def _has_recent_destabilizer (self , current_index : int ) -> bool :
@@ -1648,7 +1650,7 @@ class StructuralPattern(Enum):
16481650
16491651def validate_frequency_transition (
16501652 prev_operator : str , next_operator : str
1651- ) -> tuple [bool , str ]:
1653+ ) -> tuple [bool , str , bool ]:
16521654 """Validate structural frequency transition between consecutive operators (R5 rule).
16531655
16541656 Parameters
@@ -1660,46 +1662,56 @@ def validate_frequency_transition(
16601662
16611663 Returns
16621664 -------
1663- tuple[bool, str]
1664- (is_valid, message) where is_valid indicates if transition respects frequency
1665- harmonics, and message provides context when invalid.
1665+ tuple[bool, str, bool]
1666+ (is_valid, message, is_warning) where:
1667+ - is_valid: Always True (transitions now allowed but may warn)
1668+ - message: Context about the transition quality
1669+ - is_warning: True if transition is suboptimal (should warn)
16661670
16671671 Notes
16681672 -----
1669- Structural frequency transitions follow TNFR canonical rules:
1670- - High ↔ Medium: Bidirectional, natural energy exchange
1671- - High → Zero: High energy can pause (containment, closure via SHA terminator)
1672- - Medium ↔ Zero: Stabilization can pause, pause can resume
1673- - High ↔ High: High energy operators can chain directly
1674- - **Invalid**: Zero → High without Medium intermediary
1675-
1676- Special case: OZ → SHA (dissonance → silence) is valid and represents
1677- contained dissonance, postponed conflict, or preserved tension for later processing.
1678- This is a canonical therapeutic and crisis management pattern.
1679-
1680- This validation generates errors (not warnings) to enforce structural coherence
1681- and prevent incoherent state transitions.
1673+ Structural frequency transitions are now treated as **heuristic guidance**
1674+ rather than hard constraints. The high/medium/zero classification describes
1675+ operator characteristics but doesn't impose physical impossibility.
1676+
1677+ Frequency tiers:
1678+ - High ↔ Medium: Natural, smooth energy exchange
1679+ - High → Zero: Natural containment (e.g., OZ → SHA)
1680+ - Medium ↔ Zero: Natural stabilization/reactivation
1681+ - High ↔ High: Natural high-energy chaining
1682+ - **Suboptimal**: Zero → High (abrupt reactivation)
1683+
1684+ Zero → High transitions (e.g., SHA → AL) are **physically possible** per
1685+ ∂EPI/∂t = νf · ΔNFR - the operator can modify νf. However, they may be
1686+ structurally abrupt. A warning suggests considering an intermediate step
1687+ (e.g., SHA → NAV → AL) for smoother transitions.
1688+
1689+ This aligns frequency validation with the 4 fundamental TNFR constraints
1690+ (C1-C4) which emerge directly from physics, while treating frequency tiers
1691+ as descriptive metrics for code quality.
16821692 """
16831693 # Get frequency levels for both operators
16841694 prev_freq = STRUCTURAL_FREQUENCIES .get (prev_operator )
16851695 next_freq = STRUCTURAL_FREQUENCIES .get (next_operator )
16861696
16871697 # If either operator is unknown, skip validation (compatibility handles this)
16881698 if prev_freq is None or next_freq is None :
1689- return True , ""
1699+ return True , "" , False
16901700
1691- # Check if transition is allowed
1701+ # Check if transition is in the "smooth" set
16921702 allowed_targets = FREQUENCY_TRANSITIONS .get (prev_freq , set ())
16931703 if next_freq not in allowed_targets :
1704+ # Transition is suboptimal but not forbidden
16941705 prev_display = operator_display_name (prev_operator )
16951706 next_display = operator_display_name (next_operator )
1696- return (
1697- False ,
1698- f"Incoherent frequency transition: { prev_display } ( { prev_freq } ) → { next_display } ( { next_freq } ) . "
1699- f"Valid transitions from { prev_freq } : { ', ' .join (sorted (allowed_targets ))} " ,
1707+ warning_msg = (
1708+ f"Abrupt frequency transition: { prev_display } ( { prev_freq } ) → { next_display } ( { next_freq } ). "
1709+ f"Consider intermediate operator for smoother transition . "
1710+ f"Recommended from { prev_freq } : { ', ' .join (sorted (allowed_targets ))} "
17001711 )
1712+ return True , warning_msg , True # Valid but with warning
17011713
1702- return True , ""
1714+ return True , "" , False # Smooth transition
17031715
17041716
17051717# R5: Regenerative cycle constants and types (from cycle_detection module)
0 commit comments