@@ -3138,6 +3138,21 @@ class Mutation(Operator):
31383138 - ``threshold_met``: Boolean flag indicating threshold status
31393139 - ``threshold_ratio``: Ratio of velocity to threshold
31403140
3141+ **Bifurcation Potential Detection (∂²EPI/∂t² > τ)**:
3142+
3143+ According to AGENTS.md §U4a, ZHIR can trigger bifurcation when structural acceleration
3144+ exceeds threshold τ. The implementation detects bifurcation potential and sets telemetry
3145+ flags for validation of grammar U4a:
3146+
3147+ - **∂²EPI/∂t² > τ**: Sets ``_zhir_bifurcation_potential`` flag, logs detection
3148+ - **∂²EPI/∂t² ≤ τ**: No bifurcation potential detected
3149+
3150+ Configuration: Set ``G.graph["BIFURCATION_THRESHOLD_TAU"]`` (canonical) or
3151+ ``G.graph["ZHIR_BIFURCATION_THRESHOLD"]`` (fallback) to customize threshold (default: 0.5).
3152+
3153+ This detection enables validation of U4a requirement: "If {OZ, ZHIR}, include {THOL, IL}"
3154+ for controlled bifurcation handling.
3155+
31413156 Use Cases: Paradigm shifts, strategic pivots, adaptive responses, regime transitions,
31423157 identity transformation while maintaining continuity.
31433158
@@ -3178,6 +3193,130 @@ class Mutation(Operator):
31783193 name : ClassVar [str ] = MUTATION
31793194 glyph : ClassVar [Glyph ] = Glyph .ZHIR
31803195
3196+ def __call__ (self , G : TNFRGraph , node : Any , ** kw : Any ) -> None :
3197+ """Apply ZHIR with bifurcation potential detection.
3198+
3199+ Detects when ∂²EPI/∂t² > τ (bifurcation threshold) and sets telemetry flags
3200+ to enable validation of grammar U4a.
3201+
3202+ Parameters
3203+ ----------
3204+ G : TNFRGraph
3205+ Graph storing TNFR nodes
3206+ node : Any
3207+ Target node identifier
3208+ **kw : Any
3209+ Additional parameters including:
3210+ - tau: Bifurcation threshold (default from graph config or 0.5)
3211+ - validate_preconditions: Enable precondition checks (default True)
3212+ - collect_metrics: Enable metrics collection (default False)
3213+ """
3214+ # Compute structural acceleration before base operator
3215+ d2_epi = self ._compute_epi_acceleration (G , node )
3216+
3217+ # Get bifurcation threshold (tau) from kwargs or graph config
3218+ tau = kw .get ("tau" )
3219+ if tau is None :
3220+ # Try canonical threshold first, then operator-specific, then default
3221+ tau = float (
3222+ G .graph .get (
3223+ "BIFURCATION_THRESHOLD_TAU" ,
3224+ G .graph .get ("ZHIR_BIFURCATION_THRESHOLD" , 0.5 ),
3225+ )
3226+ )
3227+
3228+ # Apply base operator (includes glyph application, preconditions, and metrics)
3229+ super ().__call__ (G , node , ** kw )
3230+
3231+ # Detect bifurcation potential if acceleration exceeds threshold
3232+ if d2_epi > tau :
3233+ self ._detect_bifurcation_potential (G , node , d2_epi = d2_epi , tau = tau )
3234+
3235+ def _compute_epi_acceleration (self , G : TNFRGraph , node : Any ) -> float :
3236+ """Calculate ∂²EPI/∂t² from node's EPI history.
3237+
3238+ Uses finite difference approximation:
3239+ d²EPI/dt² ≈ (EPI_t - 2*EPI_{t-1} + EPI_{t-2}) / (Δt)²
3240+ For unit time steps: d²EPI/dt² ≈ EPI_t - 2*EPI_{t-1} + EPI_{t-2}
3241+
3242+ Parameters
3243+ ----------
3244+ G : TNFRGraph
3245+ Graph containing the node
3246+ node : Any
3247+ Node identifier
3248+
3249+ Returns
3250+ -------
3251+ float
3252+ Magnitude of EPI acceleration (always non-negative)
3253+ """
3254+ from ..alias import get_attr
3255+ from ..constants .aliases import ALIAS_EPI
3256+
3257+ # Get EPI history (maintained by node for temporal analysis)
3258+ history = G .nodes [node ].get ("epi_history" , [])
3259+
3260+ # Need at least 3 points for second derivative
3261+ if len (history ) < 3 :
3262+ return 0.0
3263+
3264+ # Finite difference: d²EPI/dt² ≈ (EPI_t - 2*EPI_{t-1} + EPI_{t-2})
3265+ epi_t = float (history [- 1 ])
3266+ epi_t1 = float (history [- 2 ])
3267+ epi_t2 = float (history [- 3 ])
3268+
3269+ d2_epi = epi_t - 2.0 * epi_t1 + epi_t2
3270+
3271+ return abs (d2_epi )
3272+
3273+ def _detect_bifurcation_potential (
3274+ self , G : TNFRGraph , node : Any , d2_epi : float , tau : float
3275+ ) -> None :
3276+ """Detect and record bifurcation potential when ∂²EPI/∂t² > τ.
3277+
3278+ This implements Option B (conservative detection) from the issue specification.
3279+ Sets telemetry flags and logs informative message without creating structural
3280+ variants. Enables validation of grammar U4a requirement.
3281+
3282+ Parameters
3283+ ----------
3284+ G : TNFRGraph
3285+ Graph containing the node
3286+ node : Any
3287+ Node identifier
3288+ d2_epi : float
3289+ Current EPI acceleration
3290+ tau : float
3291+ Bifurcation threshold that was exceeded
3292+ """
3293+ import logging
3294+
3295+ logger = logging .getLogger (__name__ )
3296+
3297+ # Set telemetry flags for grammar validation
3298+ G .nodes [node ]["_zhir_bifurcation_potential" ] = True
3299+ G .nodes [node ]["_zhir_d2epi" ] = d2_epi
3300+ G .nodes [node ]["_zhir_tau" ] = tau
3301+
3302+ # Record bifurcation detection event in graph for analysis
3303+ bifurcation_events = G .graph .setdefault ("zhir_bifurcation_events" , [])
3304+ bifurcation_events .append (
3305+ {
3306+ "node" : node ,
3307+ "d2_epi" : d2_epi ,
3308+ "tau" : tau ,
3309+ "timestamp" : len (G .nodes [node ].get ("glyph_history" , [])),
3310+ }
3311+ )
3312+
3313+ # Log informative message
3314+ logger .info (
3315+ f"Node { node } : ZHIR bifurcation potential detected "
3316+ f"(∂²EPI/∂t²={ d2_epi :.3f} > τ={ tau } ). "
3317+ f"Consider applying THOL for controlled bifurcation or IL for stabilization."
3318+ )
3319+
31813320 def _validate_preconditions (self , G : TNFRGraph , node : Any ) -> None :
31823321 """Validate ZHIR-specific preconditions."""
31833322 from .preconditions import validate_mutation
0 commit comments