|
1 | 1 | """Canonical grammar and sequence validation for structural operators. |
2 | 2 |
|
3 | | -This module enforces TNFR canonical grammar rules (R1-R5) to ensure that |
| 3 | +This module enforces TNFR canonical grammar rules (R1-R6) to ensure that |
4 | 4 | operator sequences respect the fundamental physics of the nodal equation: |
5 | 5 |
|
6 | 6 | ∂EPI/∂t = νf · ΔNFR(t) |
7 | 7 |
|
8 | | -Grammar Rules (R1-R5) |
| 8 | +Grammar Rules (R1-R6) |
9 | 9 | --------------------- |
10 | 10 | R1: Start operators - Must be able to generate or activate EPI |
11 | 11 | R2: Stabilizer requirement - Must contain IL (coherence) or THOL (self-organization) |
12 | 12 | R3: End operators - Must stabilize reorganization or achieve operational closure |
13 | 13 | R4: Bifurcation control - Transformers (ZHIR/THOL) require recent destabilizer |
14 | 14 | R5: Frequency transitions - Must respect structural frequency harmonics |
| 15 | +R6: Operational closure - Sequences must complete coherent reorganization cycles |
15 | 16 |
|
16 | 17 | Physics-Based Operator Derivation |
17 | 18 | ---------------------------------- |
@@ -815,6 +816,252 @@ def _has_graduated_destabilizer(self, current_index: int) -> bool: |
815 | 816 |
|
816 | 817 | return False |
817 | 818 |
|
| 819 | + def _compute_frequency_balance(self, sequence: Sequence[str]) -> float: |
| 820 | + """Calculate net structural frequency balance of sequence. |
| 821 | + |
| 822 | + Computes weighted sum of frequency levels to assess whether sequence |
| 823 | + tends toward activation (positive), silence (negative), or balance (neutral). |
| 824 | + |
| 825 | + Parameters |
| 826 | + ---------- |
| 827 | + sequence : Sequence[str] |
| 828 | + Operator sequence in canonical form |
| 829 | + |
| 830 | + Returns |
| 831 | + ------- |
| 832 | + float |
| 833 | + > 0: Sequence tends toward activation |
| 834 | + ~ 0: Sequence balanced |
| 835 | + < 0: Sequence tends toward silence/collapse |
| 836 | + |
| 837 | + Notes |
| 838 | + ----- |
| 839 | + Frequency weights from STRUCTURAL_FREQUENCIES: |
| 840 | + - high: +1.0 (AL, OZ, RA, NUL, ZHIR) |
| 841 | + - medium: +0.5 (EN, IL, UM, VAL, THOL, NAV, REMESH) |
| 842 | + - zero: -1.0 (SHA) |
| 843 | + |
| 844 | + Balance indicates net reorganization tendency according to nodal equation: |
| 845 | + ∂EPI/∂t = νf · ΔNFR. Negative balance suggests insufficient activation |
| 846 | + for sustained reorganization. |
| 847 | + """ |
| 848 | + freq_weights = {"high": 1.0, "medium": 0.5, "zero": -1.0} |
| 849 | + |
| 850 | + balance = sum( |
| 851 | + freq_weights.get(STRUCTURAL_FREQUENCIES.get(op, "medium"), 0.5) |
| 852 | + for op in sequence |
| 853 | + ) |
| 854 | + |
| 855 | + return balance / len(sequence) if sequence else 0.0 |
| 856 | + |
| 857 | + def _check_structural_convergence(self, sequence: Sequence[str]) -> bool: |
| 858 | + """Verify sequence ending doesn't create divergent state. |
| 859 | + |
| 860 | + This validates the ending operator doesn't leave the system in an |
| 861 | + actively diverging state. The operational closure balance is validated |
| 862 | + separately in _check_operational_closure(). |
| 863 | + |
| 864 | + Parameters |
| 865 | + ---------- |
| 866 | + sequence : Sequence[str] |
| 867 | + Operator sequence in canonical form |
| 868 | + |
| 869 | + Returns |
| 870 | + ------- |
| 871 | + bool |
| 872 | + True if ending is acceptable (not divergent) |
| 873 | + |
| 874 | + Notes |
| 875 | + ----- |
| 876 | + Ending operator validation: |
| 877 | + |
| 878 | + **Always acceptable:** |
| 879 | + - SHA (Silence): νf → 0 ⟹ ∂EPI/∂t → 0 (guaranteed convergence) |
| 880 | + |
| 881 | + **Acceptable if balanced (validated by closure check):** |
| 882 | + - NAV (Transition): Regime handoff provides operational closure |
| 883 | + BUT is itself a destabilizer, so requires balance |
| 884 | + - REMESH (Recursivity): Fractal completion, doesn't destabilize |
| 885 | + |
| 886 | + **Never acceptable:** |
| 887 | + - OZ (Dissonance): Leaves system with high |ΔNFR| and high νf, |
| 888 | + actively divergent without continuation |
| 889 | + |
| 890 | + Theoretical justification: |
| 891 | + From nodal equation ∂EPI/∂t = νf · ΔNFR: |
| 892 | + - SHA: νf → 0, so ∂EPI/∂t → 0 regardless of ΔNFR (strongest) |
| 893 | + - NAV: Medium νf, generates ΔNFR, requires balance (validated separately) |
| 894 | + - REMESH: Medium νf, neutral on ΔNFR, operational closure |
| 895 | + - OZ: High νf, increases |ΔNFR|, leaves divergent state |
| 896 | + |
| 897 | + The key insight: SHA guarantees convergence. NAV/REMESH provide |
| 898 | + operational closure but NAV is a destabilizer so needs balance. |
| 899 | + OZ creates divergence without providing closure mechanism. |
| 900 | + """ |
| 901 | + last_op = sequence[-1] |
| 902 | + |
| 903 | + # SHA always acceptable (convergent: νf → 0) |
| 904 | + if last_op == SILENCE: |
| 905 | + return True |
| 906 | + |
| 907 | + # OZ never acceptable as ending (divergent: high νf, high ΔNFR) |
| 908 | + if last_op == DISSONANCE: |
| 909 | + return False |
| 910 | + |
| 911 | + # NAV/REMESH acceptable as endings (operational closure) |
| 912 | + # NAV's destabilization will be validated by closure balance check |
| 913 | + # REMESH is neutral, provides fractal closure |
| 914 | + if last_op in {TRANSITION, RECURSIVITY}: |
| 915 | + return True |
| 916 | + |
| 917 | + # Unknown ending (shouldn't happen if R3 validated) |
| 918 | + return False |
| 919 | + |
| 920 | + def _check_operational_closure(self, sequence: Sequence[str]) -> bool: |
| 921 | + """Verify balance between destabilization and stabilization. |
| 922 | + |
| 923 | + Operational closure requires that destabilizing operators are balanced |
| 924 | + by stabilizing operators, preventing runaway divergence or collapse. |
| 925 | + |
| 926 | + Parameters |
| 927 | + ---------- |
| 928 | + sequence : Sequence[str] |
| 929 | + Operator sequence in canonical form |
| 930 | + |
| 931 | + Returns |
| 932 | + ------- |
| 933 | + bool |
| 934 | + True if sequence has operational closure |
| 935 | + |
| 936 | + Notes |
| 937 | + ----- |
| 938 | + Destabilizers (increase |ΔNFR|): |
| 939 | + - Strong: OZ (Dissonance) |
| 940 | + - Moderate: NAV (Transition), VAL (Expansion) |
| 941 | + - Weak: EN (Reception, context-dependent) |
| 942 | + |
| 943 | + Stabilizers (reduce |ΔNFR| or achieve closure): |
| 944 | + - IL (Coherence) |
| 945 | + - THOL (Self-organization) |
| 946 | + - SHA (Silence) |
| 947 | + - RA (Resonance) |
| 948 | + |
| 949 | + Closure conditions: |
| 950 | + 1. destabilizers ≤ stabilizers (strict balance), OR |
| 951 | + 2. Controlled mutation: ZHIR after IL (prepared phase change) |
| 952 | + |
| 953 | + Exception for controlled mutation: |
| 954 | + IL → ZHIR sequences allow destabilizers > stabilizers because |
| 955 | + coherence provides stable base for phase transformation. This |
| 956 | + represents intentional structural reorganization, not collapse. |
| 957 | + """ |
| 958 | + # Count destabilizers (all levels) |
| 959 | + from ..config.operator_names import DESTABILIZERS_ALL |
| 960 | + |
| 961 | + destabilizers = sum( |
| 962 | + 1 for op in sequence |
| 963 | + if op in DESTABILIZERS_ALL |
| 964 | + ) |
| 965 | + |
| 966 | + # Count stabilizers |
| 967 | + stabilizers = sum( |
| 968 | + 1 for op in sequence |
| 969 | + if op in {COHERENCE, SELF_ORGANIZATION, SILENCE, RESONANCE} |
| 970 | + ) |
| 971 | + |
| 972 | + # Strict balance: destabilizers ≤ stabilizers |
| 973 | + if destabilizers <= stabilizers: |
| 974 | + return True |
| 975 | + |
| 976 | + # Exception: controlled mutation (IL before ZHIR) |
| 977 | + # This allows destabilizers > stabilizers when mutation is prepared |
| 978 | + if MUTATION in sequence and COHERENCE in sequence: |
| 979 | + # Check if coherence precedes mutation (stable base for transformation) |
| 980 | + coherence_idx = sequence.index(COHERENCE) |
| 981 | + mutation_idx = sequence.index(MUTATION) |
| 982 | + if coherence_idx < mutation_idx: |
| 983 | + return True |
| 984 | + |
| 985 | + return False |
| 986 | + |
| 987 | + def _validate_operational_closure(self, sequence: Sequence[str]) -> None: |
| 988 | + """R6: Validate sequence completes coherent reorganization cycle. |
| 989 | + |
| 990 | + Ensures sequences respect conservation of structural coherence and |
| 991 | + achieve operational closure according to TNFR canonical principles. |
| 992 | + |
| 993 | + Parameters |
| 994 | + ---------- |
| 995 | + sequence : Sequence[str] |
| 996 | + Operator sequence in canonical form |
| 997 | + |
| 998 | + Raises |
| 999 | + ------ |
| 1000 | + SequenceSyntaxError |
| 1001 | + If sequence violates operational closure or convergence requirements |
| 1002 | + |
| 1003 | + Notes |
| 1004 | + ----- |
| 1005 | + R6 validates three aspects of sequence coherence: |
| 1006 | + |
| 1007 | + 1. **Structural Convergence**: Sequence must end with operator that |
| 1008 | + stabilizes reorganization (∂EPI/∂t → stable state) |
| 1009 | + |
| 1010 | + 2. **Operational Closure**: Balance between destabilizers and stabilizers |
| 1011 | + prevents divergent or collapsing trajectories |
| 1012 | + |
| 1013 | + 3. **Frequency Balance** (future): Net frequency tendency should support |
| 1014 | + coherent reorganization (currently informational only) |
| 1015 | + |
| 1016 | + Theoretical foundation: |
| 1017 | + From integrated nodal equation: |
| 1018 | + EPI(t_final) = EPI(t_initial) + ∫_{t_0}^{t_f} νf(t) · ΔNFR(t) dt |
| 1019 | + |
| 1020 | + For physical coherence: |
| 1021 | + - Integral must converge (not diverge toward collapse) |
| 1022 | + - EPI_final must be stable (sustainable coherence) |
| 1023 | + - Reorganization must close (∂EPI/∂t → 0 at sequence end) |
| 1024 | + |
| 1025 | + References: |
| 1026 | + - TNFR.pdf Section 2.1.4: Nodal stability conditions |
| 1027 | + - AGENTS.md Section 3: Canonical invariants |
| 1028 | + """ |
| 1029 | + # 1. Verify ending is not divergent (OZ rejected, NAV/REMESH/SHA OK) |
| 1030 | + if not self._check_structural_convergence(sequence): |
| 1031 | + last_op = sequence[-1] |
| 1032 | + raise SequenceSyntaxError( |
| 1033 | + index=-1, |
| 1034 | + token=None, |
| 1035 | + message=( |
| 1036 | + f"R6: Sequence ends with divergent operator {operator_display_name(last_op)}. " |
| 1037 | + f"{operator_display_name(DISSONANCE)} leaves system with high |ΔNFR| and high νf, " |
| 1038 | + f"creating actively divergent state without continuation. " |
| 1039 | + f"Acceptable endings: {operator_display_name(SILENCE)} (convergent), " |
| 1040 | + f"{operator_display_name(TRANSITION)}/{operator_display_name(RECURSIVITY)} (operational closure)." |
| 1041 | + ), |
| 1042 | + ) |
| 1043 | + |
| 1044 | + # 2. Validate operational closure (destabilizer/stabilizer balance) |
| 1045 | + if not self._check_operational_closure(sequence): |
| 1046 | + raise SequenceSyntaxError( |
| 1047 | + index=-1, |
| 1048 | + token=None, |
| 1049 | + message=( |
| 1050 | + f"R6: Sequence lacks operational closure. " |
| 1051 | + f"Destabilizers exceed stabilizers without controlled mutation. " |
| 1052 | + f"Balance destabilization ({operator_display_name(DISSONANCE)}, {operator_display_name(TRANSITION)}, " |
| 1053 | + f"{operator_display_name(EXPANSION)}) with stabilization ({operator_display_name(COHERENCE)}, " |
| 1054 | + f"{operator_display_name(SELF_ORGANIZATION)}, {operator_display_name(SILENCE)})." |
| 1055 | + ), |
| 1056 | + ) |
| 1057 | + |
| 1058 | + # 3. Compute frequency balance (informational - for future warnings) |
| 1059 | + # Currently not enforced as error, but could be used for telemetry |
| 1060 | + freq_balance = self._compute_frequency_balance(sequence) |
| 1061 | + # Store for metadata/telemetry |
| 1062 | + # Future: Could add warnings for extremely negative balance (< -0.5) |
| 1063 | + # indicating tendency toward collapse |
| 1064 | + |
818 | 1065 | def _finalize(self, names: Sequence[str]) -> None: |
819 | 1066 | if self._unknown_tokens: |
820 | 1067 | ordered = ", ".join(sorted({token for _, token in self._unknown_tokens})) |
@@ -865,6 +1112,9 @@ def _finalize(self, names: Sequence[str]) -> None: |
865 | 1112 | # R5: Validate regenerative cycles if pattern is REGENERATIVE |
866 | 1113 | if self._detected_pattern == StructuralPattern.REGENERATIVE: |
867 | 1114 | self._validate_regenerative_cycle() |
| 1115 | + |
| 1116 | + # R6: Validate operational closure and coherence conservation |
| 1117 | + self._validate_operational_closure(self._canonical) |
868 | 1118 |
|
869 | 1119 | def _validate_regenerative_cycle(self) -> None: |
870 | 1120 | """Validate regenerative cycle structural requirements (R5). |
|
0 commit comments