Skip to content

Commit 3fe6a54

Browse files
Spider-local (Claude Sonnet 4.6)claude
andcommitted
tel: thread grammar version through convergence stack
ConvergenceDetector now accepts grammar_version (default TEL_GRAMMAR_v1) and prefixes it into _derive_seed() — full-vector convergence hash is now version-aware, consistent with derive_c_seed() in convergence_split.py. grammar_version is stored on the detector, surfaced in get_state(), and passed explicitly to ConvergenceSplit in validate_convergence.py so the chain is traceable end-to-end. Validation header now prints grammar version. Addresses Bess memo 2026-05-25 — prevents silent mismatches when nodes run different grammar versions. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
1 parent 4ac52a3 commit 3fe6a54

3 files changed

Lines changed: 14 additions & 5 deletions

File tree

tel_deploy/convergence.py

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@
33
import logging
44
from typing import Callable, Awaitable
55

6+
from tel_deploy.convergence_split import GRAMMAR_VERSION
7+
68
log = logging.getLogger("tel.convergence")
79

810
# Trefoil period — 4 consecutive zero-delta passes = converged
@@ -24,15 +26,17 @@ class ConvergenceDetector:
2426
The collapse point is deterministic. Velocity varies, destination does not.
2527
"""
2628

27-
def __init__(self, test_fn: Callable[[], Awaitable[list]]):
29+
def __init__(self, test_fn: Callable[[], Awaitable[list]], grammar_version: str = GRAMMAR_VERSION):
2830
"""
2931
Args:
3032
test_fn: Async callable that runs the 27-test suite and returns
3133
a state vector of layer classifications.
3234
e.g. ["L1", "L3", "L4", "L2", "L4", ...] (len=27)
3335
(33 total tests, 6 excluded oscillators = 27 active positions)
36+
grammar_version: TEL grammar version string — must match across all mesh nodes.
3437
"""
3538
self.test_fn = test_fn
39+
self.grammar_version = grammar_version
3640
self.history = []
3741
self.converged = False
3842
self.stable_vector = None
@@ -45,7 +49,7 @@ def _compute_delta(self, v1: list, v2: list) -> int:
4549
return sum(1 for a, b in zip(v1, v2) if a != b)
4650

4751
def _derive_seed(self, vector: list) -> str:
48-
raw = self._vector_to_bytes(vector)
52+
raw = (self.grammar_version + json.dumps(vector, separators=(",", ":"))).encode("utf-8")
4953
return hashlib.sha3_256(raw).hexdigest()
5054

5155
async def run(self, max_passes: int = 20) -> bool:
@@ -118,6 +122,7 @@ def get_seed(self) -> str:
118122

119123
def get_state(self) -> dict:
120124
return {
125+
"grammar_version": self.grammar_version,
121126
"converged": self.converged,
122127
"passes_run": len(self.history),
123128
"stable_vector": self.stable_vector,

tel_deploy/convergence_split.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,9 @@
88
# All nodes must use the same version to derive the same C-seed.
99
# History:
1010
# v1 2026-05-16 Initial versioned grammar: 33 tests, 6 excluded, 23C/4B split.
11+
# v1 2026-05-25 Version prefix applied to ConvergenceDetector._derive_seed()
12+
# — full-vector convergence hash now versioned, consistent with c_seed.
13+
# grammar_version threaded explicitly through validate_convergence.py.
1114
GRAMMAR_VERSION = "TEL_GRAMMAR_v1"
1215

1316
# LAYER C: Universal positions — identical across all models

tel_deploy/validate_convergence.py

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
1616

1717
from tel_deploy.convergence import ConvergenceDetector
18-
from tel_deploy.convergence_split import ConvergenceSplit
18+
from tel_deploy.convergence_split import ConvergenceSplit, GRAMMAR_VERSION
1919
from tel_deploy.test_runner import run_convergence_pass
2020

2121
# --- Endpoints ---
@@ -85,7 +85,7 @@ async def test_fn():
8585
print(f" [{label}] running convergence pass...")
8686
return await run_convergence_pass(endpoint, api_key, model=model, azure=True)
8787

88-
detector = ConvergenceDetector(test_fn)
88+
detector = ConvergenceDetector(test_fn, grammar_version=GRAMMAR_VERSION)
8989
success = await detector.run(max_passes=20)
9090

9191
result = {
@@ -104,7 +104,7 @@ async def test_fn():
104104
}
105105

106106
if success:
107-
split = ConvergenceSplit(detector.stable_vector)
107+
split = ConvergenceSplit(detector.stable_vector, grammar_version=detector.grammar_version)
108108
result["c_seed"] = split.c_seed
109109
result["b_vector"] = split.b_vector
110110
result["b_fingerprint"] = split.b_fingerprint
@@ -160,6 +160,7 @@ async def main():
160160

161161
log.info(f"Log file: {LOG_PATH}")
162162
print("TEL Mesh — Multi-Model Convergence Validation")
163+
print(f"Grammar version: {GRAMMAR_VERSION}")
163164
print(f"Known C-seed prefix: {KNOWN_C_SEED}")
164165
print(f"Deployments: {len(DEPLOYMENTS)}")
165166
for label, model, region in DEPLOYMENTS:

0 commit comments

Comments
 (0)