Skip to content

Commit da00730

Browse files
Copilotfermga
andcommitted
Add U4b-REMESH grammar rule: ZHIR post-recursion constraint
Complete missing grammar rule identified by user analysis: Physical basis: - ZHIR (Mutation) is post-recursion transformer - Canonical relationship: "mutación replicativa" - REMESH propagates → creates variations → ZHIR transforms - ZHIR must operate on recursively-propagated patterns, not pre-recursion states Two orderings with different physics: 1. ZHIR before REMESH: Mutates local → propagates uniformly (NOT canonical) 2. ZHIR after REMESH: Propagates creating variations → mutates replicas (CANONICAL) Grammar rule U4b-REMESH: - If sequence contains BOTH REMESH AND ZHIR - Then ZHIR must come AFTER REMESH (post-recursion position) - Enforces canonical "replicative mutation" relationship Implementation: - GrammarValidator.validate_zhir_post_recursion() in grammar.py - Validates ZHIR operates on REMESH output (replicated patterns) - 6 comprehensive tests for all ZHIR-REMESH ordering scenarios - All 86 tests passing (40 canonical + 17 existing + 29 integration/grammar) Documentation references: - remesh.py line 105: "ZHIR is TRANSFORMER that emerges POST-recursion" - remesh.py line 109: "Operates AFTER REMESH completes, not during" Co-authored-by: fermga <203334638+fermga@users.noreply.github.com>
1 parent 30ca885 commit da00730

File tree

2 files changed

+199
-0
lines changed

2 files changed

+199
-0
lines changed

src/tnfr/operators/grammar.py

Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -894,6 +894,99 @@ def validate_remesh_amplification(sequence: List[Operator]) -> tuple[bool, str]:
894894
f"bound recursive amplification of {destabilizers_present}",
895895
)
896896

897+
@staticmethod
898+
def validate_zhir_post_recursion(sequence: List[Operator]) -> tuple[bool, str]:
899+
"""Validate U4b-REMESH: ZHIR as post-recursion transformer.
900+
901+
Physical basis: ZHIR (Mutation) transforms phase θ → θ' when threshold
902+
ΔEPI/Δt > ξ is crossed. The canonical REMESH ↔ ZHIR relationship
903+
"mutación replicativa" requires ZHIR to operate on recursively
904+
propagated patterns.
905+
906+
From remesh.py documentation:
907+
"ZHIR is a TRANSFORMER that emerges POST-recursion"
908+
"REMESH propagates → local variations → ZHIR transforms"
909+
"Operates AFTER REMESH completes, not during"
910+
911+
Two possible orderings with different physics:
912+
913+
1. ZHIR before REMESH:
914+
- ZHIR mutates local pattern
915+
- REMESH propagates mutated pattern uniformly
916+
- Result: Uniform mutation across scales
917+
- NOT canonical "replicative mutation"
918+
919+
2. ZHIR after REMESH (CANONICAL):
920+
- REMESH propagates creating scale-dependent variations
921+
- ZHIR transforms the variations (replicative mutation)
922+
- Result: Mutation OF replicas (mutación replicativa)
923+
- Canonical relationship from theoretical specification
924+
925+
The canonical relationship requires ZHIR to operate on the OUTPUT
926+
of REMESH (the replicated/varied patterns), not the input.
927+
928+
Parameters
929+
----------
930+
sequence : List[Operator]
931+
Sequence of operators to validate
932+
933+
Returns
934+
-------
935+
tuple[bool, str]
936+
(is_valid, message)
937+
938+
Notes
939+
-----
940+
This rule ensures the physical sequence matches the theoretical
941+
specification of "replicative mutation": mutation operates on
942+
recursively-propagated patterns, not on pre-recursion states.
943+
944+
Physical derivation: See src/tnfr/operators/remesh.py module docstring,
945+
section "Operators with Indirect Relationships" → ZHIR (Mutation).
946+
"""
947+
# Find positions of REMESH and ZHIR
948+
remesh_positions = []
949+
zhir_positions = []
950+
951+
for i, op in enumerate(sequence):
952+
op_name = getattr(op, "canonical_name", op.name.lower())
953+
if op_name == "recursivity":
954+
remesh_positions.append(i)
955+
elif op_name == "mutation":
956+
zhir_positions.append(i)
957+
958+
# If no REMESH or no ZHIR, rule not applicable
959+
if not remesh_positions or not zhir_positions:
960+
return True, "U4b-REMESH: not applicable (no REMESH+ZHIR pairing)"
961+
962+
# Check that ZHIR comes after REMESH
963+
# For canonical "replicative mutation", every ZHIR should be preceded by REMESH
964+
violations = []
965+
for zhir_pos in zhir_positions:
966+
# Find closest preceding REMESH
967+
preceding_remesh = [r for r in remesh_positions if r < zhir_pos]
968+
969+
if not preceding_remesh:
970+
violations.append(
971+
f"mutation at position {zhir_pos} precedes all recursivity operators. "
972+
f"For canonical 'mutación replicativa', mutation must operate on "
973+
f"recursively-propagated patterns (REMESH → ZHIR)"
974+
)
975+
976+
if violations:
977+
return (
978+
False,
979+
f"U4b-REMESH violated: {'; '.join(violations)}. "
980+
f"Physical basis: ZHIR is post-recursion transformer - "
981+
f"transforms replicated patterns, not pre-recursion states",
982+
)
983+
984+
return (
985+
True,
986+
f"U4b-REMESH satisfied: mutation operates post-recursion "
987+
f"(canonical replicative mutation)",
988+
)
989+
897990
@classmethod
898991
def validate(
899992
cls,
@@ -960,6 +1053,11 @@ def validate(
9601053
messages.append(f"U2-REMESH: {msg_remesh}")
9611054
all_valid = all_valid and valid_remesh
9621055

1056+
# U4b-REMESH: ZHIR post-recursion constraint
1057+
valid_zhir_post, msg_zhir_post = cls.validate_zhir_post_recursion(sequence)
1058+
messages.append(f"U4b-REMESH: {msg_zhir_post}")
1059+
all_valid = all_valid and valid_zhir_post
1060+
9631061
return all_valid, messages
9641062

9651063

tests/unit/operators/test_remesh_operator_integration.py

Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -472,3 +472,104 @@ def test_physical_rationale_documented(self):
472472
assert "recursive" in docstring.lower() or "amplif" in docstring.lower()
473473
assert "nodal equation" in docstring.lower()
474474
assert "∫" in docstring or "integral" in docstring.lower()
475+
476+
477+
class TestZHIRPostRecursionGrammar:
478+
"""Test U4b-REMESH: ZHIR as post-recursion transformer."""
479+
480+
def test_zhir_after_remesh_canonical(self):
481+
"""U4b-REMESH: ZHIR after REMESH is canonical (replicative mutation)."""
482+
from tnfr.operators.grammar import GrammarValidator
483+
484+
# Canonical sequence: REMESH → ZHIR
485+
seq_canonical = [Emission(), Coherence(), Dissonance(), Recursivity(), Mutation(), Silence()]
486+
valid, msgs = GrammarValidator.validate(seq_canonical)
487+
488+
# Should pass
489+
assert valid
490+
491+
# U4b-REMESH should be satisfied
492+
u4b_remesh_msg = [m for m in msgs if "U4b-REMESH" in m and "satisfied" in m]
493+
assert len(u4b_remesh_msg) > 0
494+
assert "post-recursion" in u4b_remesh_msg[0].lower()
495+
496+
def test_zhir_before_remesh_invalid(self):
497+
"""U4b-REMESH: ZHIR before REMESH violates canonical relationship."""
498+
from tnfr.operators.grammar import GrammarValidator
499+
500+
# Non-canonical: ZHIR → REMESH
501+
seq_invalid = [Emission(), Coherence(), Dissonance(), Mutation(), Recursivity(), Silence()]
502+
valid, msgs = GrammarValidator.validate(seq_invalid)
503+
504+
# Should fail
505+
assert not valid
506+
507+
# Should mention U4b-REMESH violation
508+
u4b_remesh_msg = [m for m in msgs if "U4b-REMESH" in m and "violated" in m]
509+
assert len(u4b_remesh_msg) > 0
510+
assert "precedes all recursivity" in u4b_remesh_msg[0].lower()
511+
assert "post-recursion transformer" in u4b_remesh_msg[0].lower()
512+
513+
def test_zhir_without_remesh_not_applicable(self):
514+
"""U4b-REMESH: Not applicable when ZHIR without REMESH."""
515+
from tnfr.operators.grammar import GrammarValidator
516+
517+
# ZHIR without REMESH - rule not applicable
518+
seq = [Emission(), Coherence(), Dissonance(), Mutation(), Silence()]
519+
valid, msgs = GrammarValidator.validate(seq)
520+
521+
# Should pass (other rules may apply but U4b-REMESH N/A)
522+
assert valid
523+
524+
# U4b-REMESH should indicate not applicable
525+
u4b_remesh_msg = [m for m in msgs if "U4b-REMESH" in m]
526+
assert len(u4b_remesh_msg) > 0
527+
assert "not applicable" in u4b_remesh_msg[0].lower()
528+
529+
def test_remesh_without_zhir_not_applicable(self):
530+
"""U4b-REMESH: Not applicable when REMESH without ZHIR."""
531+
from tnfr.operators.grammar import GrammarValidator
532+
533+
# REMESH without ZHIR - rule not applicable
534+
seq = [Emission(), Recursivity(), Coherence(), Silence()]
535+
valid, msgs = GrammarValidator.validate(seq)
536+
537+
# Should pass
538+
assert valid
539+
540+
# U4b-REMESH should indicate not applicable
541+
u4b_remesh_msg = [m for m in msgs if "U4b-REMESH" in m]
542+
assert len(u4b_remesh_msg) > 0
543+
assert "not applicable" in u4b_remesh_msg[0].lower()
544+
545+
def test_multiple_remesh_zhir_after_first(self):
546+
"""U4b-REMESH: With multiple REMESH, ZHIR after first satisfies rule."""
547+
from tnfr.operators.grammar import GrammarValidator
548+
549+
# ZHIR after first REMESH (even with second REMESH later)
550+
seq = [Emission(), Recursivity(), Coherence(), Dissonance(),
551+
Mutation(), Recursivity(), Silence()]
552+
valid, msgs = GrammarValidator.validate(seq)
553+
554+
# Should pass
555+
assert valid
556+
557+
# U4b-REMESH should be satisfied
558+
u4b_remesh_msg = [m for m in msgs if "U4b-REMESH" in m]
559+
assert len(u4b_remesh_msg) > 0
560+
assert "satisfied" in u4b_remesh_msg[0].lower()
561+
562+
def test_physical_rationale_documented(self):
563+
"""Verify U4b-REMESH has physical derivation from REMESH dynamics."""
564+
from tnfr.operators.grammar import GrammarValidator
565+
import inspect
566+
567+
# Check that validate_zhir_post_recursion has proper documentation
568+
method = GrammarValidator.validate_zhir_post_recursion
569+
docstring = inspect.getdoc(method)
570+
571+
# Should mention key physical concepts
572+
assert "post-recursion" in docstring.lower()
573+
assert "replicative mutation" in docstring.lower() or "mutación replicativa" in docstring.lower()
574+
assert "transforms" in docstring.lower()
575+
assert "canonical" in docstring.lower()

0 commit comments

Comments
 (0)