Skip to content

Commit 9ddcc43

Browse files
Copilotfermga
andcommitted
Implement THOL encapsulation per operational fractality physics
Co-authored-by: fermga <203334638+fermga@users.noreply.github.com>
1 parent 31d52ed commit 9ddcc43

File tree

1 file changed

+22
-4
lines changed

1 file changed

+22
-4
lines changed

src/tnfr/operators/grammar.py

Lines changed: 22 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -696,6 +696,7 @@ class _SequenceAutomaton:
696696
"_destabilizer_context",
697697
"_thol_stack",
698698
"_thol_subsequences",
699+
"_thol_encapsulated_indices",
699700
)
700701

701702
def __init__(self) -> None:
@@ -721,6 +722,7 @@ def __init__(self) -> None:
721722
# THOL recursive validation: Track nested THOL blocks and their subsequences
722723
self._thol_stack: list[int] = [] # Stack of THOL opening indices
723724
self._thol_subsequences: dict[int, list[str]] = {} # Subsequences by opening index
725+
self._thol_encapsulated_indices: set[int] = set() # Indices inside THOL windows
724726

725727
def run(self, names: Sequence[str]) -> None:
726728
if not names:
@@ -815,6 +817,9 @@ def _consume(self, token: str, index: int) -> None:
815817
current_thol = self._thol_stack[-1]
816818
window_content = self._thol_subsequences[current_thol]
817819

820+
# Mark this operator as encapsulated in THOL window
821+
self._thol_encapsulated_indices.add(index)
822+
818823
# Check if this is the first operator in window
819824
if len(window_content) == 0:
820825
# First operator after THOL opening
@@ -825,6 +830,8 @@ def _consume(self, token: str, index: int) -> None:
825830
# Close THOL immediately and process operator in parent context
826831
self._thol_stack.pop()
827832
self._open_thol = bool(self._thol_stack)
833+
# Unmark as encapsulated since it's part of parent context
834+
self._thol_encapsulated_indices.discard(index)
828835
# Don't add to window - process normally in parent context
829836
# (will be processed by subsequent validation logic)
830837
return
@@ -1257,12 +1264,23 @@ def _finalize(self, names: Sequence[str]) -> None:
12571264
# Already validated in _accept() for first operator
12581265

12591266
# C1.2: End validation (legacy R3)
1260-
if self._canonical[-1] not in VALID_END_OPERATORS:
1267+
# TNFR Encapsulation: Check last operator NOT inside THOL window
1268+
# Sub-EPIs are independent nodes (operational fractality), so operators
1269+
# inside THOL windows don't count toward main sequence ending.
1270+
last_non_encapsulated_index = len(self._canonical) - 1
1271+
for i in range(len(self._canonical) - 1, -1, -1):
1272+
if i not in self._thol_encapsulated_indices:
1273+
last_non_encapsulated_index = i
1274+
break
1275+
1276+
if self._canonical[last_non_encapsulated_index] not in VALID_END_OPERATORS:
12611277
cierre = _format_token_group(_CANONICAL_END)
12621278
raise SequenceSyntaxError(
1263-
index=len(names) - 1,
1264-
token=names[-1],
1265-
message=f"C1: sequence must end with {cierre} (EXISTENCE & CLOSURE constraint)",
1279+
index=last_non_encapsulated_index,
1280+
token=names[last_non_encapsulated_index],
1281+
message=f"C1: sequence must end with {cierre} (EXISTENCE & CLOSURE constraint). "
1282+
f"Operators inside {operator_display_name(SELF_ORGANIZATION)} windows are encapsulated "
1283+
f"(operational fractality) and don't count as sequence ending.",
12661284
)
12671285

12681286
# NOTE: C1.3 Reception→Coherence segment validation removed

0 commit comments

Comments
 (0)