@@ -714,10 +714,23 @@ def validate_self_organization(G: "TNFRGraph", node: "NodeId") -> None:
714714 3. Structural reorganization capacity (νf > 0)
715715 4. Network connectivity for metabolism (degree ≥ 1)
716716 5. EPI history for acceleration computation (≥3 points)
717+ 6. **NEW**: Bifurcation threshold check (∂²EPI/∂t² vs τ) with telemetry
717718
718719 Also detects and records the destabilizer type that enabled this self-organization
719720 for telemetry and structural tracing purposes.
720721
722+ **Bifurcation Threshold Validation (∂²EPI/∂t² > τ):**
723+
724+ According to TNFR.pdf §2.2.10, THOL bifurcation occurs only when structural
725+ acceleration exceeds threshold τ. This function now explicitly validates this
726+ condition and sets telemetry flags:
727+
728+ - If ∂²EPI/∂t² > τ: Bifurcation will occur (normal THOL behavior)
729+ - If ∂²EPI/∂t² ≤ τ: THOL executes but no sub-EPIs generated (warning logged)
730+
731+ The validation is NON-BLOCKING (warning only) because THOL can meaningfully
732+ execute without bifurcation - it still applies coherence and metabolic effects.
733+
721734 Parameters
722735 ----------
723736 G : TNFRGraph
@@ -749,6 +762,10 @@ def validate_self_organization(G: "TNFRGraph", node: "NodeId") -> None:
749762 Allow isolated nodes for internal-only bifurcation
750763 THOL_METABOLIC_ENABLED : bool, default True
751764 Require metabolic network context
765+ BIFURCATION_THRESHOLD_TAU : float, default 0.1
766+ Bifurcation threshold for ∂²EPI/∂t² (see THOL_BIFURCATION_THRESHOLD)
767+ THOL_BIFURCATION_THRESHOLD : float, default 0.1
768+ Alias for BIFURCATION_THRESHOLD_TAU (operator-specific config)
752769 """
753770 import logging
754771
@@ -821,6 +838,53 @@ def validate_self_organization(G: "TNFRGraph", node: "NodeId") -> None:
821838 # R4 Extended: Detect and record destabilizer type for telemetry
822839 _record_destabilizer_context (G , node , logger )
823840
841+ # NEW: Bifurcation threshold validation (∂²EPI/∂t² > τ)
842+ # This is NON-BLOCKING - THOL can execute without bifurcation
843+ # Note: SelfOrganization uses its own _compute_epi_acceleration which looks at 'epi_history'
844+ # while compute_d2epi_dt2 looks at '_epi_history'. We check both for compatibility.
845+
846+ # Get EPI history from node (try both keys for compatibility)
847+ history = G .nodes [node ].get ("_epi_history" ) or G .nodes [node ].get ("epi_history" , [])
848+
849+ # Compute d²EPI/dt² directly from history (same logic as both functions)
850+ if len (history ) >= 3 :
851+ epi_t = float (history [- 1 ])
852+ epi_t1 = float (history [- 2 ])
853+ epi_t2 = float (history [- 3 ])
854+ d2_epi_signed = epi_t - 2.0 * epi_t1 + epi_t2
855+ d2_epi = abs (d2_epi_signed )
856+ else :
857+ # Insufficient history - should have been caught earlier, but handle gracefully
858+ d2_epi = 0.0
859+
860+ # Get bifurcation threshold from graph configuration
861+ # Try BIFURCATION_THRESHOLD_TAU first (canonical), then THOL_BIFURCATION_THRESHOLD
862+ tau = G .graph .get ("BIFURCATION_THRESHOLD_TAU" )
863+ if tau is None :
864+ tau = float (G .graph .get ("THOL_BIFURCATION_THRESHOLD" , 0.1 ))
865+ else :
866+ tau = float (tau )
867+
868+ # Check if bifurcation threshold will be exceeded
869+ if d2_epi <= tau :
870+ # Log warning but allow execution - THOL can be meaningful without bifurcation
871+ logger .warning (
872+ f"Node { node } : THOL applied with ∂²EPI/∂t²={ d2_epi :.3f} ≤ τ={ tau :.3f} . "
873+ f"No bifurcation will occur (empty THOL window expected). "
874+ f"Sub-EPIs will not be generated. "
875+ f"Consider stronger destabilizer (OZ, VAL) to increase acceleration."
876+ )
877+ # Set telemetry flag for post-hoc analysis
878+ G .nodes [node ]["_thol_no_bifurcation_expected" ] = True
879+ else :
880+ # Clear flag if previously set
881+ G .nodes [node ]["_thol_no_bifurcation_expected" ] = False
882+ logger .debug (
883+ f"Node { node } : THOL bifurcation threshold exceeded "
884+ f"(∂²EPI/∂t²={ d2_epi :.3f} > τ={ tau :.3f} ). "
885+ f"Sub-EPI generation expected."
886+ )
887+
824888
825889def _record_destabilizer_context (
826890 G : "TNFRGraph" , node : "NodeId" , logger : "logging.Logger"
0 commit comments