#!/usr/bin/env python3 """check_core_server_parity.py — live HTTP parity: the Python oracle (core_server.make_server) vs the GO port (src/go/core-server serve), byte-exact bodies under pinned uuid/time (scripts/cs_freeze_seams). Both servers run the SAME spec-built stub operator; the Go process gets the identical spec via RUNE_CS_OPERATOR_SPEC plus the id/created pins via RUNE_CS_CHUNK_HEX / RUNE_FROZEN_NOW, so every response body — including chat.completion ids, created epochs, NDJSON framing, error envelopes, and Python json.dumps separator bytes — must match the oracle BYTE FOR BYTE (not modulo-volatile like the canary). Run: python3 scripts/check_core_server_parity.py """ from __future__ import annotations import http.client import json import os import subprocess import sys import threading import time from pathlib import Path REPO = Path(__file__).resolve().parents[1] # Pull in the freeze rig (auto-imported when PYTHONPATH includes it; the # explicit import covers bare invocation) — it pins uuid/time on the # oracle module BEFORE import and exposes the stub operator + constants. sys.path.insert(0, str(REPO / "scripts" / "cs_freeze_seams")) import sitecustomize # noqa: E402 (the rig; pins apply on first import) from rune import core_server # noqa: E402 (after the rig pins) FROZEN_HEX = sitecustomize.FROZEN_HEX FROZEN_NOW = sitecustomize.FROZEN_NOW SPEC = { "models": [{"name": "stub-a", "size_gb": 4.1}, {"size_gb": 2}, {}], "status": {"identity": "rune", "role": "operator", "current_task": None, "active_model": "stub-a", "tasks_pending": 0, "tasks_completed": 3}, "send": {"task_id": "task-1", "status": "completed", "model": "stub-model", "user_text": "Canned reply.", "degraded": [], "iterations_used": 1}, "window": [{"role": "user", "content": "m1"}, {"role": "assistant", "content": "m2"}, {"role": "user", "content": "m3"}], } SPEC_RAISE = {"raise_status": "boom", "raise_send": "boom send"} PASS = 0 FAIL = 0 def test(name: str, ok: bool, detail: str = "") -> None: global PASS, FAIL if ok: PASS += 1 print(f" ✓ {name}") else: FAIL += 1 print(f" ✗ {name} — {detail}") def request(port: int, method: str, path: str, body: str | None = None): """One raw request → (status, content-type, body-bytes) or NO_RESPONSE.""" try: conn = http.client.HTTPConnection("127.0.0.1", port, timeout=10) headers = {} data = None if body is not None: data = body.encode() headers["Content-Type"] = "application/json" conn.request(method, path, body=data, headers=headers) resp = conn.getresponse() out = (resp.status, resp.getheader("Content-Type"), resp.read()) conn.close() return out except Exception: # noqa: BLE001 — the crash-parity class IS the contract return ("NO_RESPONSE", None, b"") def wait_ready(port: int, timeout: float = 10.0) -> bool: deadline = time.time() + timeout while time.time() < deadline: r = request(port, "GET", "/v1/status") if r[0] != "NO_RESPONSE": return True time.sleep(0.1) return False def free_port() -> int: import socket s = socket.socket() s.bind(("127.0.0.1", 0)) port = s.getsockname()[1] s.close() return port class PyServer: """The oracle on a real socket with the rig's spec-built stub.""" def __init__(self, spec: dict): self.port = free_port() self.server = core_server.make_server( "127.0.0.1", self.port, sitecustomize._stub_operator(spec)) self.thread = threading.Thread(target=self.server.serve_forever, daemon=True) def __enter__(self) -> int: self.thread.start() assert wait_ready(self.port) return self.port def __exit__(self, *exc) -> None: self.server.shutdown() class GoServer: def __init__(self, spec: dict): self.port = free_port() env = dict(os.environ) env["RUNE_CS_OPERATOR_SPEC"] = json.dumps(spec) env["RUNE_CS_CHUNK_HEX"] = FROZEN_HEX env["RUNE_FROZEN_NOW"] = FROZEN_NOW self.proc = subprocess.Popen( ["/tmp/cs-parity-serve", "--host", "127.0.0.1", "--port", str(self.port)], env=env, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, text=True) def __enter__(self) -> int: assert wait_ready(self.port) return self.port def __exit__(self, *exc) -> None: self.proc.terminate() try: self.proc.wait(timeout=5) except subprocess.TimeoutExpired: self.proc.kill() CHAT_BODY = json.dumps({"model": "m2", "messages": [ {"role": "user", "content": "first"}, {"role": "assistant", "content": "mid"}, {"role": "user", "content": "final"}]}) def cases() -> list[tuple[str, str, str, str | None]]: return [ ("models", "GET", "/v1/models", None), ("status", "GET", "/v1/status", None), ("conversation", "GET", "/v1/conversation?channel=ops&n=2", None), ("conversation-defaults", "GET", "/v1/conversation", None), ("conversation-bad-n", "GET", "/v1/conversation?n=abc", None), ("conversation-pct", "GET", "/v1/conversation?channel=%C3%A9+d", None), ("models-fullpath-mismatch", "GET", "/v1/models?x=1", None), ("not-found", "GET", "/v1/nope", None), ("approve", "POST", "/v1/approvals/resolve", '{"approved": true}'), ("approve-truthy-string", "POST", "/v1/approvals/resolve", '{"approved": "yes"}'), ("approve-empty-body", "POST", "/v1/approvals/resolve", ""), ("approve-bad-json", "POST", "/v1/approvals/resolve", "not json"), ("approve-trailing-comma", "POST", "/v1/approvals/resolve", '{"approved": true,}'), ("chat", "POST", "/v1/chat/completions", CHAT_BODY), ("chat-stream", "POST", "/v1/chat/completions", '{"messages": [{"role": "user", "content": "streamed"}], "stream": true}'), ("chat-stream-truthy-string", "POST", "/v1/chat/completions", '{"messages": [{"role": "user", "content": "x"}], "stream": "yes"}'), ("chat-no-user", "POST", "/v1/chat/completions", '{"messages": [{"role": "assistant", "content": "no"}]}'), ("chat-numeric-content", "POST", "/v1/chat/completions", '{"messages": [{"role": "user", "content": 5}]}'), ("chat-crash-parity", "POST", "/v1/chat/completions", '{"messages": "abc"}'), ("chat-model-empty", "POST", "/v1/chat/completions", '{"model": "", "messages": [{"role": "user", "content": "x"}]}'), ("post-not-found", "POST", "/v1/other", "{}"), ] def sweep(py_port: int, go_port: int) -> None: for name, method, path, body in cases(): py = request(py_port, method, path, body) go = request(go_port, method, path, body) ok = py == go detail = "" if not ok: detail = (f"py=({py[0]!r},{py[1]!r}) {py[2][:160]!r} " f"go=({go[0]!r},{go[1]!r}) {go[2][:160]!r}") test(f"parity: {name} byte-identical", ok, detail) def main() -> int: print("=" * 55) print("core_server live parity — Python oracle vs GO port (pinned)") print("=" * 55) go_dir = REPO / "src" / "go" / "core-server" r = subprocess.run(["go", "build", "-o", "/tmp/cs-parity-serve", "."], cwd=go_dir, capture_output=True, text=True, timeout=300) test("go build exits 0", r.returncode == 0, r.stderr[-300:]) with PyServer(SPEC) as py_port, GoServer(SPEC) as go_port: print(f"py oracle :{py_port} go port :{go_port}") sweep(py_port, go_port) with PyServer(SPEC_RAISE) as py_port, GoServer(SPEC_RAISE) as go_port: py = request(py_port, "GET", "/v1/status") go = request(go_port, "GET", "/v1/status") test("parity: raise-status 500 envelope byte-identical", py == go, f"py={py[2][:160]!r} go={go[2][:160]!r}") py = request(py_port, "POST", "/v1/chat/completions", '{"messages": [{"role": "user", "content": "x"}]}') go = request(go_port, "POST", "/v1/chat/completions", '{"messages": [{"role": "user", "content": "x"}]}') test("parity: raise-send 500 envelope byte-identical", py == go, f"py={py[2][:160]!r} go={go[2][:160]!r}") print(f"\nRESULTS: {PASS} pass, {FAIL} fail") return 1 if FAIL else 0 if __name__ == "__main__": sys.exit(main())