feat(core_server): close PARTIAL gaps — dichotomy wave
Port the 6 audit-missing fns name-preserving into src/go/core-server (make_server, log_message, _send_json, _send_ndjson, do_GET, do_POST; main via Main/os.Args surface): the full HTTP sidecar surface with byte-identical wire bodies (vendored pyjson PyDump: json.dumps ', '/': ' separators + ensure_ascii + PyDict insertion order), CPython json scanner error messages, int()/parse_qs/truthiness/or-laws, str[:200] codepoint truncation, and the AttributeError crash-parity class. Truth: 57-case shared-harness envelope (scripts/gen_core_server_truth.py under scripts/cs_freeze_seams — pinned uuid/time on module attrs, rig-bound probe seams exercising the REAL handler methods) verified byte-identical x5; go test 9/9 (verbatim TestFrozenTruthReplay + 8 quirk tests); CI replay binary 57/57; live HTTP parity 24/24 byte-identical (scripts/check_core_server_parity.py — bodies exact under pinned id/created, not modulo-volatile); canary_rune_serve 28/28; gofmt/vet/build clean; ruff clean; shims pytest zero-delta vs stashed base (9 fresh-worktree sibling-kernel failures pre-existing). Dichotomy audit core_server 6/12 PARTIAL -> 12/12 FULL (107/192).
This commit is contained in:
1 parent
c5c64684b3
commit
65ceff40b9
20 files changed
+4228
-245
No files matched your search
@@ -2,14 +2,14 @@
|
||||
|
||||
_Regenerate with `python3 scripts/dichotomy_audit.py --write`. A module is FULL only when 80%+ of its Python functions have name-preserving counterparts in the Go/Rust implementation — directories without the functions are facades and count as NOT TOUCHED._
|
||||
|
||||
**FULL completion: 62%** (119/192 runtime modules at FULL)
|
||||
**FULL completion: 62%** (120/192 runtime modules at FULL)
|
||||
|
||||
Function-level detail: 73% (1680/2288 runtime functions matched) — secondary; includes in-flight work
|
||||
Function-level detail: 74% (1690/2288 runtime functions matched) — secondary; includes in-flight work
|
||||
|
||||
| Status | Modules | Lines | Meaning |
|
||||
|---|---|---|---|
|
||||
| FULL | 119 | 49107 | 80%+ of functions ported |
|
||||
| PARTIAL | 34 | 14925 | 20–80% — real logic exists, gaps named below |
|
||||
| FULL | 120 | 49341 | 80%+ of functions ported |
|
||||
| PARTIAL | 33 | 14691 | 20–80% — real logic exists, gaps named below |
|
||||
| NOT TOUCHED | 39 | 11471 | no name-preserving implementation |
|
||||
| Total runtime | 192 | 75503 | |
|
||||
|
||||
@@ -61,14 +61,13 @@ Function-level detail: 73% (1680/2288 runtime functions matched) — secondary;
|
||||
|
||||
| Module | Lines | Coverage | Missing (first named) |
|
||||
|---|---|---|---|
|
||||
| rune_operator | 2746 | 25/68 | _build_model_router, _sorted_tool_names, _filter_safety_noise, _wire_auto_filer, _reconcile_guarded, get_operator |
|
||||
| rune_operator | 2746 | 29/68 | _build_model_router, _sorted_tool_names, _filter_safety_noise, _wire_auto_filer, _reconcile_guarded, get_conversation_window |
|
||||
| memory_store | 2333 | 30/68 | _ctx_budget, now_iso, ensure_memory_type_column, _embed, _temp_json, _entities_to_json |
|
||||
| warm_router | 1518 | 17/33 | _ensure_use_history_table, record_outcome, record_neuron_fire, base_level_activation, _db, _aaak |
|
||||
| tool_executor | 1836 | 33/47 | _execute_tool_now, serialize_prepared_tool, deserialize_prepared_tool, confirm_write, _resolve_execution_cwd, _run_command |
|
||||
| time_awareness | 402 | 12/20 | _clock_args, _ta_core, get_time_awareness, to_dict, _to_state, _apply_state |
|
||||
| intent_continuity | 284 | 6/13 | _now, _raise_core_err, to_dict, from_dict, save, _ledger_state |
|
||||
| audit_ledger | 212 | 2/8 | _compute_hash, get_entries, log_promotion, log_gate_result, log_drift_alert, main |
|
||||
| core_server | 234 | 6/12 | make_server, log_message, _send_json, _send_ndjson, do_GET, do_POST |
|
||||
| voicebox | 164 | 5/11 | _run, start_recording, stop_recording, _transcribe_and_type, on_press, on_release |
|
||||
| persona_definition | 210 | 2/7 | _by_topic, compile_full, emit_constitution, emit_identity, emit_persona |
|
||||
| drift_observability | 399 | 5/9 | _do_core, main, _load_alerts, _save_alerts |
|
||||
@@ -187,6 +186,7 @@ Function-level detail: 73% (1680/2288 runtime functions matched) — secondary;
|
||||
| iteration | 261 | 7/8 | RUST |
|
||||
| model_registry | 253 | 8/8 | GO |
|
||||
| event_bus | 244 | 7/7 | GO |
|
||||
| core_server | 234 | 12/12 | GO |
|
||||
| openai_compat_transport | 227 | 9/9 | GO |
|
||||
| cognitive_budgeter | 221 | 3/3 | RUST |
|
||||
| evidence_probe | 213 | 9/9 | GO |
|
||||
|
||||
@@ -0,0 +1,227 @@
|
||||
#!/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())
|
||||
@@ -0,0 +1,187 @@
|
||||
# Freeze rig for the core_server dichotomy gap-closing (dichotomy wave
|
||||
# #2479 gap port, contract factory-gap-cs). Auto-imported on interpreter
|
||||
# start (PYTHONPATH=scripts/cs_freeze_seams:src) by the truth generator
|
||||
# (scripts/gen_core_server_truth.py), the shared harness's --verify mode,
|
||||
# and the live-parity sweep, so frozen truth is byte-reproducible in any
|
||||
# process:
|
||||
#
|
||||
# * HOME pinned to a fresh throwaway (freeze_rigs.pin_home) — hermetic,
|
||||
# though core_server derives no home paths (belt and braces).
|
||||
# * uuid/time pinned ON the oracle module BEFORE any capture:
|
||||
# core_server imports uuid/time at MODULE level, so patching the
|
||||
# module attributes reaches every call site (unlike function-local
|
||||
# imports). uuid.uuid4() → a stub whose .hex is FROZEN_HEX (the
|
||||
# battery mirrors it via the frozenChunkHex constant in
|
||||
# src/go/core-server/main.go — keep them in lockstep); time.time() →
|
||||
# FROZEN_EPOCH (int(FROZEN_EPOCH) == the envelope's frozen_now).
|
||||
# * The handler/server probe seams (_do_get_probe etc.) exercise the
|
||||
# REAL oracle methods against a recording fake handler — no sockets,
|
||||
# no threads, fully deterministic.
|
||||
# * The stub operator is spec-built (scripts/gen_core_server_truth.py
|
||||
# passes plain-JSON specs in the case args; the rig rebuilds live
|
||||
# operators from them — the callable-corpora law).
|
||||
|
||||
import copy
|
||||
import io
|
||||
import os
|
||||
import sys
|
||||
import types
|
||||
|
||||
_REPO = os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
|
||||
sys.path.insert(0, os.path.join(_REPO, "scripts"))
|
||||
sys.path.insert(0, os.path.join(_REPO, "src"))
|
||||
|
||||
from freeze_rigs import pin_home # noqa: E402
|
||||
|
||||
pin_home("cs")
|
||||
|
||||
FROZEN_EPOCH = 1791964800.0 # int() == 1791964800 == 2026-10-14T08:00:00+00:00
|
||||
FROZEN_NOW = "2026-10-14T08:00:00+00:00"
|
||||
FROZEN_HEX = "636f72652d7365727665722d667265657a6521" # "core-server-freeze!"
|
||||
|
||||
|
||||
class _FrozenUuid4:
|
||||
"""uuid.uuid4() under the pin — .hex is the frozen constant."""
|
||||
|
||||
hex = FROZEN_HEX # noqa: A003 — mirrors uuid.UUID.hex
|
||||
|
||||
|
||||
import rune.core_server as _cs # noqa: E402 (after the HOME pin)
|
||||
|
||||
_cs.uuid = types.SimpleNamespace(uuid4=lambda: _FrozenUuid4())
|
||||
_cs.time = types.SimpleNamespace(time=lambda: FROZEN_EPOCH)
|
||||
|
||||
|
||||
# ── the recording fake handler ──────────────────────────────────────────────
|
||||
|
||||
|
||||
class _RecHandler:
|
||||
"""The BaseHTTPRequestHandler surface do_GET/do_POST/_send_json/
|
||||
_send_ndjson touch: path, rfile, headers, core.operator, and the
|
||||
send_response/send_header/end_headers/wfile call order."""
|
||||
|
||||
def __init__(self, path="", operator=None, rfile=None, headers=None):
|
||||
self.path = path
|
||||
self.wfile = io.BytesIO()
|
||||
self.rfile = rfile
|
||||
self.headers = headers if headers is not None else {}
|
||||
self.core = types.SimpleNamespace(operator=operator)
|
||||
self.status = None
|
||||
self.sent_headers = []
|
||||
# Bind the REAL oracle send methods onto the fake so do_GET/do_POST
|
||||
# exercise them (they record through this object's send_* surface).
|
||||
self._send_json = types.MethodType(_cs.CoreHandler._send_json, self)
|
||||
self._send_ndjson = types.MethodType(_cs.CoreHandler._send_ndjson, self)
|
||||
|
||||
def send_response(self, status):
|
||||
self.status = status
|
||||
|
||||
def send_header(self, key, value):
|
||||
self.sent_headers.append([key, value])
|
||||
|
||||
def end_headers(self):
|
||||
pass
|
||||
|
||||
|
||||
# ── the spec-built stub operator (mirrors Go SpecOperator) ─────────────────
|
||||
|
||||
|
||||
def _stub_operator(spec):
|
||||
class _Op:
|
||||
def list_models(self):
|
||||
return copy.deepcopy(spec.get("models", []))
|
||||
|
||||
def get_status(self):
|
||||
if "raise_status" in spec:
|
||||
raise RuntimeError(spec["raise_status"])
|
||||
return copy.deepcopy(spec.get("status", {}))
|
||||
|
||||
def send_task(self, description, model=None):
|
||||
if "raise_send" in spec:
|
||||
raise RuntimeError(spec["raise_send"])
|
||||
out = copy.deepcopy(spec.get("send", {}))
|
||||
out["echo"] = description
|
||||
out["model_arg"] = model
|
||||
return out
|
||||
|
||||
def conversation_window(self, channel="operator", n=8):
|
||||
rows = copy.deepcopy(spec.get("window", []))[:n]
|
||||
rows.append({"seen_channel": channel, "seen_n": n})
|
||||
return rows
|
||||
|
||||
def resolve_pending_approval(self, approved):
|
||||
return {"resolved": bool(approved)}
|
||||
|
||||
return _Op()
|
||||
|
||||
|
||||
# ── probe seams the shared harness freezes ──────────────────────────────────
|
||||
|
||||
|
||||
def _models_view_probe(spec):
|
||||
return _cs.models_view(_stub_operator(spec))
|
||||
|
||||
|
||||
def _make_server_probe(spec):
|
||||
srv = _cs.make_server("127.0.0.1", 0, _stub_operator(spec))
|
||||
out = {
|
||||
"type": type(srv).__name__,
|
||||
"handler": srv.RequestHandlerClass.__name__,
|
||||
"daemon_threads": srv.daemon_threads,
|
||||
"allow_reuse_address": srv.allow_reuse_address,
|
||||
"family": srv.address_family,
|
||||
"operator": "bound",
|
||||
}
|
||||
srv.server_close()
|
||||
return out
|
||||
|
||||
|
||||
def _log_message_probe(fmt, args):
|
||||
handler = _RecHandler()
|
||||
return _cs.CoreHandler.log_message(handler, fmt, *args)
|
||||
|
||||
|
||||
def _send_json_probe(status, body):
|
||||
h = _RecHandler()
|
||||
_cs.CoreHandler._send_json(h, status, body)
|
||||
return {"status": h.status, "headers": h.sent_headers,
|
||||
"body": h.wfile.getvalue().decode()}
|
||||
|
||||
|
||||
def _send_ndjson_probe(status, items):
|
||||
h = _RecHandler()
|
||||
_cs.CoreHandler._send_ndjson(h, status, items)
|
||||
return {"status": h.status, "headers": h.sent_headers,
|
||||
"body": h.wfile.getvalue().decode()}
|
||||
|
||||
|
||||
def _do_get_probe(path, spec):
|
||||
h = _RecHandler(path=path, operator=_stub_operator(spec))
|
||||
try:
|
||||
_cs.CoreHandler.do_GET(h)
|
||||
return {"status": h.status, "headers": h.sent_headers,
|
||||
"body": h.wfile.getvalue().decode()}
|
||||
except Exception as exc: # noqa: BLE001 — the uncaught-crash class IS the contract
|
||||
return {"unhandled": str(exc)}
|
||||
|
||||
|
||||
def _do_post_probe(path, content_length, body_text, spec):
|
||||
rfile = io.BytesIO(body_text.encode())
|
||||
headers = {} if content_length is None else {"Content-Length": content_length}
|
||||
h = _RecHandler(path=path, operator=_stub_operator(spec), rfile=rfile,
|
||||
headers=headers)
|
||||
try:
|
||||
_cs.CoreHandler.do_POST(h)
|
||||
return {"status": h.status, "headers": h.sent_headers,
|
||||
"body": h.wfile.getvalue().decode()}
|
||||
except Exception as exc: # noqa: BLE001 — the uncaught-crash class IS the contract
|
||||
return {"unhandled": str(exc)}
|
||||
|
||||
|
||||
_cs._models_view_probe = _models_view_probe
|
||||
_cs._make_server_probe = _make_server_probe
|
||||
_cs._log_message_probe = _log_message_probe
|
||||
_cs._send_json_probe = _send_json_probe
|
||||
_cs._send_ndjson_probe = _send_ndjson_probe
|
||||
_cs._do_get_probe = _do_get_probe
|
||||
_cs._do_post_probe = _do_post_probe
|
||||
@@ -0,0 +1,164 @@
|
||||
#!/usr/bin/env python3
|
||||
"""gen_core_server_truth.py — freeze core_server oracle truth (dichotomy
|
||||
wave #2479 gap port, contract factory-gap-cs).
|
||||
|
||||
Runs under the cs freeze rig (PYTHONPATH=scripts/cs_freeze_seams:src) so
|
||||
uuid/time are pinned on the oracle module and every case is
|
||||
deterministic. Covers EVERY ported function and branch: the envelope
|
||||
transforms (_envelope_error, models_view, chat_completion, chat_chunks)
|
||||
directly, and the handler/server layer (log_message, _send_json,
|
||||
_send_ndjson, do_GET, do_POST, make_server) through the rig-bound probe
|
||||
seams that exercise the REAL oracle methods against a recording fake
|
||||
handler. The Go battery (src/go/core-server/truth_test.go) and the CI
|
||||
replay binary rebuild every row through the ported functions.
|
||||
|
||||
Usage:
|
||||
PYTHONPATH=scripts/cs_freeze_seams:src python3 scripts/gen_core_server_truth.py
|
||||
"""
|
||||
from __future__ import annotations
|
||||
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
sys.path.insert(0, str(Path(__file__).resolve().parents[1] / "scripts"))
|
||||
|
||||
from freeze_truth import freeze, write_envelope # noqa: E402
|
||||
|
||||
FROZEN_NOW = "2026-10-14T08:00:00+00:00" # int epoch 1791964800 (rig FROZEN_EPOCH)
|
||||
TRUTH = Path(__file__).resolve().parents[1] / "src" / "go" / "core-server" / "truth.json"
|
||||
|
||||
SPEC_MODELS = {"models": [{"name": "stub-a", "size_gb": 4.1}, {"size_gb": 2}, {}]}
|
||||
SPEC_STATUS = {"status": {"identity": "rune", "role": "operator", "current_task": None,
|
||||
"active_model": "stub-a", "tasks_pending": 0,
|
||||
"tasks_completed": 3}}
|
||||
SPEC_CHAT = {"send": {"task_id": "task-1", "status": "completed",
|
||||
"model": "stub-model", "user_text": "Canned reply.",
|
||||
"degraded": [], "iterations_used": 1}}
|
||||
SPEC_WINDOW = {"window": [{"role": "user", "content": "m1"},
|
||||
{"role": "assistant", "content": "m2"},
|
||||
{"role": "user", "content": "m3"}]}
|
||||
SPEC_FULL = {**SPEC_MODELS, **SPEC_STATUS, **SPEC_CHAT, **SPEC_WINDOW}
|
||||
|
||||
|
||||
def post(path, body, spec, cl="auto"):
|
||||
"""do_POST probe args; cl="auto" carries the exact byte length."""
|
||||
cl_val = str(len(body.encode())) if cl == "auto" else cl
|
||||
return (path, cl_val, body, spec)
|
||||
|
||||
|
||||
CASES: list = [
|
||||
# ── envelope transforms ────────────────────────────────────────────
|
||||
("_envelope_error", ("not_found", "no route /v1/definitely-not-here")),
|
||||
("_envelope_error", ("internal_error", "x" * 250)), # no truncation at the source
|
||||
("_models_view_probe", (SPEC_MODELS,)), # missing-name → "" law, order preserved
|
||||
("_models_view_probe", ({"models": []},)), # empty list
|
||||
("chat_completion", ({"model": "stub-model", "user_text": "hello world",
|
||||
"task_id": "t1"}, None)),
|
||||
("chat_completion", ({"user_text": "x"}, "fallback-model")), # result model missing
|
||||
("chat_completion", ({"model": "", "user_text": "y"}, "fb")), # falsy result model
|
||||
("chat_completion", ({"model": 5, "user_text": "z"}, None)), # non-string truthy
|
||||
("chat_completion", ({"user_text": None}, "m")), # present-null content passes
|
||||
("chat_completion", ({"model": "m"}, "fb")), # missing user_text → ""
|
||||
("chat_chunks", ({"model": "stub-model", "user_text": "streamed"}, None)),
|
||||
("chat_chunks", ({"user_text": "x"}, "fb")),
|
||||
("chat_chunks", ({"user_text": "héllo ✓"}, "m")), # ensure_ascii bytes in NDJSON
|
||||
# ── handler surface ────────────────────────────────────────────────
|
||||
("_log_message_probe", ('"GET %s" %d', ["/v1/models", 200])),
|
||||
("_send_json_probe", (200, {"object": "list", "data": []})), # ", "/": " law
|
||||
("_send_json_probe", (404, {"error": {"code": "not_found",
|
||||
"message": "no route /x"}})),
|
||||
("_send_json_probe", (200, {"msg": "héllo ✓"})), # ensure_ascii
|
||||
("_send_ndjson_probe", (200, [{"a": 1}, {"b": [1, 2]}])),
|
||||
("_send_ndjson_probe", (200, [])), # empty body, Content-Length 0
|
||||
("_make_server_probe", (SPEC_FULL,)),
|
||||
# ── do_GET ─────────────────────────────────────────────────────────
|
||||
("_do_get_probe", ("/v1/models", SPEC_MODELS)),
|
||||
("_do_get_probe", ("/v1/status", SPEC_STATUS)),
|
||||
("_do_get_probe", ("/v1/status", {"raise_status": "boom"})),
|
||||
("_do_get_probe", ("/v1/status", {"raise_status": "y" * 250})), # [:200] runes
|
||||
("_do_get_probe", ("/v1/conversation?channel=ops&n=2", SPEC_WINDOW)),
|
||||
("_do_get_probe", ("/v1/conversation", SPEC_WINDOW)), # defaults operator/8
|
||||
("_do_get_probe", ("/v1/conversation?n=abc", SPEC_WINDOW)), # int ValueError → 500
|
||||
("_do_get_probe", ("/v1/conversation?channel=", SPEC_WINDOW)), # blank dropped
|
||||
("_do_get_probe", ("/v1/conversation?n=99&n=abc", SPEC_WINDOW)), # first value
|
||||
("_do_get_probe", ("/v1/conversation?n=+12", SPEC_WINDOW)), # '+' → ' ' → int strip
|
||||
("_do_get_probe", ("/v1/conversation?n=-1", SPEC_WINDOW)), # negative slice
|
||||
("_do_get_probe", ("/v1/conversation?channel=%C3%A9+d", SPEC_WINDOW)), # %XX + '+'
|
||||
("_do_get_probe", ("/v1/models?x=1", SPEC_MODELS)), # full-path mismatch → 404
|
||||
("_do_get_probe", ("/v1/nope", SPEC_FULL)),
|
||||
# ── do_POST ────────────────────────────────────────────────────────
|
||||
("_do_post_probe", post("/v1/approvals/resolve", '{"approved": true}', {})),
|
||||
("_do_post_probe", post("/v1/approvals/resolve", '{"approved": 1}', {})), # truthy
|
||||
("_do_post_probe", post("/v1/approvals/resolve", '{"approved": "yes"}', {})),
|
||||
("_do_post_probe", post("/v1/approvals/resolve", '{"approved": 0}', {})), # falsy
|
||||
("_do_post_probe", post("/v1/approvals/resolve", "", {})), # empty → {} → False
|
||||
("_do_post_probe", post("/v1/approvals/resolve", '{"approved": true}', {}, cl="-5")),
|
||||
("_do_post_probe", post("/v1/approvals/resolve", "not json", {})),
|
||||
("_do_post_probe", post("/v1/approvals/resolve", "{}", {}, cl="x")), # CL garbage
|
||||
("_do_post_probe", post("/v1/approvals/resolve", "{bad", {})),
|
||||
("_do_post_probe", post(
|
||||
"/v1/chat/completions",
|
||||
'{"model": "m2", "messages": [{"role": "user", "content": "first"}, '
|
||||
'{"role": "assistant", "content": "mid"}, {"role": "user", "content": "final"}]}',
|
||||
SPEC_CHAT)), # LAST user text + model passthrough
|
||||
("_do_post_probe", post(
|
||||
"/v1/chat/completions",
|
||||
'{"messages": [{"role": "user", "content": "streamed"}], "stream": true}',
|
||||
SPEC_CHAT)),
|
||||
("_do_post_probe", post(
|
||||
"/v1/chat/completions",
|
||||
'{"messages": [{"role": "user", "content": "x"}], "stream": false}',
|
||||
SPEC_CHAT)),
|
||||
("_do_post_probe", post(
|
||||
"/v1/chat/completions",
|
||||
'{"messages": [{"role": "user", "content": "x"}], "stream": "yes"}',
|
||||
SPEC_CHAT)), # truthy string
|
||||
("_do_post_probe", post(
|
||||
"/v1/chat/completions",
|
||||
'{"messages": [{"role": "assistant", "content": "no user here"}]}',
|
||||
SPEC_CHAT)), # 400 user-role law
|
||||
("_do_post_probe", post(
|
||||
"/v1/chat/completions",
|
||||
'{"messages": [{"role": "user", "content": 5}]}',
|
||||
SPEC_CHAT)), # non-string content passes through (echo 5)
|
||||
("_do_post_probe", post(
|
||||
"/v1/chat/completions",
|
||||
'{"messages": "abc"}',
|
||||
SPEC_CHAT)), # uncaught AttributeError crash class
|
||||
("_do_post_probe", post(
|
||||
"/v1/chat/completions",
|
||||
'{"model": "", "messages": [{"role": "user", "content": "x"}]}',
|
||||
SPEC_CHAT)), # "" → None → model_arg null
|
||||
("_do_post_probe", post(
|
||||
"/v1/chat/completions",
|
||||
'{"model": 5, "messages": [{"role": "user", "content": "x"}]}',
|
||||
SPEC_CHAT)), # truthy non-string model
|
||||
("_do_post_probe", post(
|
||||
"/v1/chat/completions",
|
||||
'{"messages": [{"role": "user", "content": "x"}]}',
|
||||
{"raise_send": "boom"})), # send_task raise → 500 envelope
|
||||
("_do_post_probe", post(
|
||||
"/v1/chat/completions",
|
||||
'{"messages": [{"role": "user", "content": "c"}],}',
|
||||
SPEC_CHAT)), # trailing comma message
|
||||
("_do_post_probe", post(
|
||||
"/v1/chat/completions",
|
||||
'{"messages": []} x',
|
||||
SPEC_CHAT)), # extra data message
|
||||
("_do_post_probe", post(
|
||||
"/v1/chat/completions",
|
||||
'{"model" "m"}',
|
||||
SPEC_CHAT)), # ':' delimiter message
|
||||
("_do_post_probe", post("/v1/other", "{}", SPEC_FULL)), # 404
|
||||
]
|
||||
|
||||
|
||||
def main() -> int:
|
||||
truth = freeze("rune.core_server", CASES, oracle_module="rune.core_server")
|
||||
write_envelope(FROZEN_NOW, truth, TRUTH)
|
||||
print(f"wrote {len(truth)} cases to {TRUTH}")
|
||||
return 0
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
sys.exit(main())
|
||||
+1304
-197
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1,245 @@
|
||||
package main
|
||||
|
||||
// pyjson.go — vendored Python json.dumps-compatible serialization (the
|
||||
// established src/go/provider-registry/pyjson.go artifact, adapted) plus the
|
||||
// ordered parser batteries use to replay Python insertion-ordered objects.
|
||||
// json.dumps defaults: ", " / ": " separators, ensure_ascii=True.
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"math"
|
||||
"sort"
|
||||
"strconv"
|
||||
"strings"
|
||||
"unicode/utf16"
|
||||
)
|
||||
|
||||
// PyDict is an insertion-ordered object (Python dict semantics).
|
||||
type PyDict struct {
|
||||
Keys []string
|
||||
Vals map[string]any
|
||||
}
|
||||
|
||||
func NewPyDict() *PyDict { return &PyDict{Vals: map[string]any{}} }
|
||||
|
||||
func (d *PyDict) Set(k string, v any) {
|
||||
if _, ok := d.Vals[k]; !ok {
|
||||
d.Keys = append(d.Keys, k)
|
||||
}
|
||||
d.Vals[k] = v
|
||||
}
|
||||
|
||||
func (d *PyDict) Get(k string) (any, bool) {
|
||||
v, ok := d.Vals[k]
|
||||
return v, ok
|
||||
}
|
||||
|
||||
func (d *PyDict) Len() int { return len(d.Keys) }
|
||||
|
||||
// PyDump — json.dumps(obj) with Python's default separators and
|
||||
// ensure_ascii escaping. Maps serialize with sorted keys (Go map law);
|
||||
// use PyDict where Python insertion order must be reproduced.
|
||||
func PyDump(v any) string {
|
||||
s, _ := pyDumpTry(v)
|
||||
return s
|
||||
}
|
||||
|
||||
// pyDumpTry reports (ok=false) when the value has no Python-JSON
|
||||
// equivalent — the caller applies the oracle's degrade law.
|
||||
func pyDumpTry(v any) (string, bool) {
|
||||
var b strings.Builder
|
||||
if err := pyWrite(&b, v); err != nil {
|
||||
return "", false
|
||||
}
|
||||
return b.String(), true
|
||||
}
|
||||
|
||||
func pyWrite(b *strings.Builder, v any) error {
|
||||
switch x := v.(type) {
|
||||
case nil:
|
||||
b.WriteString("null")
|
||||
case bool:
|
||||
if x {
|
||||
b.WriteString("true")
|
||||
} else {
|
||||
b.WriteString("false")
|
||||
}
|
||||
case string:
|
||||
pyWriteString(b, x)
|
||||
case int:
|
||||
b.WriteString(strconv.Itoa(x))
|
||||
case int64:
|
||||
b.WriteString(strconv.FormatInt(x, 10))
|
||||
case float64:
|
||||
b.WriteString(pyFloat(x))
|
||||
case json.Number:
|
||||
b.WriteString(x.String())
|
||||
case *PyDict:
|
||||
b.WriteByte('{')
|
||||
for i, k := range x.Keys {
|
||||
if i > 0 {
|
||||
b.WriteString(", ")
|
||||
}
|
||||
pyWriteString(b, k)
|
||||
b.WriteString(": ")
|
||||
if err := pyWrite(b, x.Vals[k]); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
b.WriteByte('}')
|
||||
case map[string]any:
|
||||
keys := make([]string, 0, len(x))
|
||||
for k := range x {
|
||||
keys = append(keys, k)
|
||||
}
|
||||
sortStrings(keys)
|
||||
b.WriteByte('{')
|
||||
for i, k := range keys {
|
||||
if i > 0 {
|
||||
b.WriteString(", ")
|
||||
}
|
||||
pyWriteString(b, k)
|
||||
b.WriteString(": ")
|
||||
if err := pyWrite(b, x[k]); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
b.WriteByte('}')
|
||||
case []any:
|
||||
b.WriteByte('[')
|
||||
for i, e := range x {
|
||||
if i > 0 {
|
||||
b.WriteString(", ")
|
||||
}
|
||||
if err := pyWrite(b, e); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
b.WriteByte(']')
|
||||
default:
|
||||
// Unknown Go types have no Python json equivalent; the caller
|
||||
// applies the oracle's degrade law.
|
||||
return fmt.Errorf("type not JSON-serializable: %T", v)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func pyFloat(f float64) string {
|
||||
if f == math.Trunc(f) && math.Abs(f) < 1e15 {
|
||||
return strconv.FormatFloat(f, 'f', 1, 64) // Python repr(2.0) → "2.0"
|
||||
}
|
||||
return strconv.FormatFloat(f, 'g', -1, 64)
|
||||
}
|
||||
|
||||
func pyWriteString(b *strings.Builder, s string) {
|
||||
b.WriteByte('"')
|
||||
for _, r := range s {
|
||||
switch r {
|
||||
case '"':
|
||||
b.WriteString("\\\"")
|
||||
case '\\':
|
||||
b.WriteString("\\\\")
|
||||
case '\n':
|
||||
b.WriteString("\\n")
|
||||
case '\r':
|
||||
b.WriteString("\\r")
|
||||
case '\t':
|
||||
b.WriteString("\\t")
|
||||
case '\b':
|
||||
b.WriteString("\\b")
|
||||
case '\f':
|
||||
b.WriteString("\\f")
|
||||
default:
|
||||
if r < 0x20 || r > 0x7e {
|
||||
writeUnicodeEscape(b, r)
|
||||
} else {
|
||||
b.WriteRune(r)
|
||||
}
|
||||
}
|
||||
}
|
||||
b.WriteByte('"')
|
||||
}
|
||||
|
||||
func writeUnicodeEscape(b *strings.Builder, r rune) {
|
||||
if r > 0xffff {
|
||||
hi, lo := utf16.EncodeRune(r)
|
||||
fmt.Fprintf(b, "\\u%04x\\u%04x", hi, lo)
|
||||
return
|
||||
}
|
||||
fmt.Fprintf(b, "\\u%04x", r)
|
||||
}
|
||||
|
||||
func sortStrings(s []string) { sort.Strings(s) }
|
||||
|
||||
// ParsePyJSON — order-preserving JSON decode: objects → *PyDict,
|
||||
// arrays → []any, integral numbers → int, others → float64.
|
||||
func ParsePyJSON(data []byte) (any, error) {
|
||||
dec := json.NewDecoder(strings.NewReader(string(data)))
|
||||
dec.UseNumber()
|
||||
v, err := decodePyValue(dec)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return v, nil
|
||||
}
|
||||
|
||||
func decodePyValue(dec *json.Decoder) (any, error) {
|
||||
tok, err := dec.Token()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return decodePyToken(dec, tok)
|
||||
}
|
||||
|
||||
func decodePyToken(dec *json.Decoder, tok json.Token) (any, error) {
|
||||
switch t := tok.(type) {
|
||||
case json.Delim:
|
||||
switch t {
|
||||
case '{':
|
||||
d := NewPyDict()
|
||||
for dec.More() {
|
||||
keyTok, err := dec.Token()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
key, _ := keyTok.(string)
|
||||
val, err := decodePyValue(dec)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
d.Set(key, val)
|
||||
}
|
||||
if _, err := dec.Token(); err != nil { // closing }
|
||||
return nil, err
|
||||
}
|
||||
return d, nil
|
||||
case '[':
|
||||
arr := []any{}
|
||||
for dec.More() {
|
||||
val, err := decodePyValue(dec)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
arr = append(arr, val)
|
||||
}
|
||||
if _, err := dec.Token(); err != nil { // closing ]
|
||||
return nil, err
|
||||
}
|
||||
return arr, nil
|
||||
}
|
||||
return nil, fmt.Errorf("unexpected delim %v", t)
|
||||
case json.Number:
|
||||
if n, err := t.Int64(); err == nil && !strings.ContainsAny(t.String(), ".eE") {
|
||||
return n, nil
|
||||
}
|
||||
f, err := t.Float64()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return f, nil
|
||||
case string, bool, nil:
|
||||
return tok, nil
|
||||
}
|
||||
return nil, fmt.Errorf("unexpected token %v", tok)
|
||||
}
|
||||
+1794
-42
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1,204 @@
|
||||
package main
|
||||
|
||||
// truth_test.go — the parity gate. Replays every case from truth.json
|
||||
// (the shared freeze_truth.py envelope: pinned uuid/time + rig-bound
|
||||
// probe seams, generated by scripts/gen_core_server_truth.py against
|
||||
// the live Python oracle under scripts/cs_freeze_seams) VERBATIM
|
||||
// through the ported Go functions. A divergence fails the port, not
|
||||
// the oracle.
|
||||
|
||||
import (
|
||||
_ "embed"
|
||||
"encoding/json"
|
||||
"strconv"
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
//go:embed truth.json
|
||||
var frozenTruthJSON string
|
||||
|
||||
// TestFrozenTruthReplay — the frozen oracle truth, verbatim, in the
|
||||
// recorded call order, against the ported functions.
|
||||
func TestFrozenTruthReplay(t *testing.T) {
|
||||
var env struct {
|
||||
FrozenNow string `json:"frozen_now"`
|
||||
Cases []frozenCase `json:"cases"`
|
||||
}
|
||||
if err := json.Unmarshal([]byte(frozenTruthJSON), &env); err != nil {
|
||||
t.Fatalf("decode truth envelope: %v", err)
|
||||
}
|
||||
if len(env.Cases) == 0 {
|
||||
t.Fatal("no truth cases — the parity gate is empty")
|
||||
}
|
||||
applyFrozenSeams(env.FrozenNow)
|
||||
for i, c := range env.Cases {
|
||||
actual, err := replayCase(&c)
|
||||
if err != nil {
|
||||
t.Fatalf("case %d fn=%s: %v", i, c.Fn, err)
|
||||
}
|
||||
want, err := ParsePyJSON(c.Expected)
|
||||
if err != nil {
|
||||
t.Fatalf("case %d fn=%s: decode expected: %v", i, c.Fn, err)
|
||||
}
|
||||
if PyDump(actual) != PyDump(want) {
|
||||
t.Errorf("case %d fn=%s: got %s want %s", i, c.Fn, PyDump(actual), PyDump(want))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TestPyDumpsSeparatorsAndAscii — the json.dumps defaults: ", " / ": "
|
||||
// separators, ensure_ascii escapes (byte-parity wire law).
|
||||
func TestPyDumpsSeparatorsAndAscii(t *testing.T) {
|
||||
nested := pyDict1("error", pyDict2("code", "not_found", "message", "no route /x"))
|
||||
want := `{"error": {"code": "not_found", "message": "no route /x"}}`
|
||||
if got := PyDump(nested); got != want {
|
||||
t.Errorf("separators:\n got %s\nwant %s", got, want)
|
||||
}
|
||||
u := pyDict1("msg", "héllo ✓")
|
||||
wantU := `{"msg": "h\u00e9llo \u2713"}`
|
||||
if got := PyDump(u); got != wantU {
|
||||
t.Errorf("ensure_ascii:\n got %s\nwant %s", got, wantU)
|
||||
}
|
||||
}
|
||||
|
||||
// TestTruncateRunesCodepoints — str[:200] slices CODEPOINTS.
|
||||
func TestTruncateRunesCodepoints(t *testing.T) {
|
||||
if got := truncateRunes(strings.Repeat("y", 250), 200); len([]rune(got)) != 200 {
|
||||
t.Errorf("ascii: got %d runes", len([]rune(got)))
|
||||
}
|
||||
é := strings.Repeat("é", 300) // 2 bytes each
|
||||
if got := truncateRunes(é, 200); len([]rune(got)) != 200 {
|
||||
t.Errorf("multibyte counted as bytes: got %d runes", len([]rune(got)))
|
||||
}
|
||||
if got := truncateRunes("short", 200); got != "short" {
|
||||
t.Errorf("short string mangled: %q", got)
|
||||
}
|
||||
}
|
||||
|
||||
// TestPyIntLiteralPythonMessages — the exact ValueError text.
|
||||
func TestPyIntLiteralPythonMessages(t *testing.T) {
|
||||
for _, c := range []struct{ in, msg string }{
|
||||
{"abc", "invalid literal for int() with base 10: 'abc'"},
|
||||
{" 8x ", "invalid literal for int() with base 10: ' 8x '"},
|
||||
{"", "invalid literal for int() with base 10: ''"},
|
||||
} {
|
||||
_, err := pyIntLiteral(c.in)
|
||||
if err == nil || err.Error() != c.msg {
|
||||
t.Errorf("pyIntLiteral(%q): got %v want %s", c.in, err, c.msg)
|
||||
}
|
||||
}
|
||||
for _, c := range []struct {
|
||||
in string
|
||||
want int
|
||||
}{{" 8 ", 8}, {"+8", 8}, {"-2", -2}, {"8_0", 80}, {"0", 0}} {
|
||||
got, err := pyIntLiteral(c.in)
|
||||
if err != nil || got != c.want {
|
||||
t.Errorf("pyIntLiteral(%q): got %d,%v want %d", c.in, got, err, c.want)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TestPyJsonLoadPythonErrorMessages — the CPython scanner messages
|
||||
// (verified against python3 3.14: the message is the contract).
|
||||
func TestPyJsonLoadPythonErrorMessages(t *testing.T) {
|
||||
for _, c := range []struct{ in, msg string }{
|
||||
{"not json", "Expecting value: line 1 column 1 (char 0)"},
|
||||
{"{bad", "Expecting property name enclosed in double quotes: line 1 column 2 (char 1)"},
|
||||
{`{"a" 1}`, "Expecting ':' delimiter: line 1 column 6 (char 5)"},
|
||||
{`{"a":1} x`, "Extra data: line 1 column 9 (char 8)"},
|
||||
{`{"a":1,}`, "Illegal trailing comma before end of object: line 1 column 7 (char 6)"},
|
||||
{"[1,]", "Illegal trailing comma before end of array: line 1 column 3 (char 2)"},
|
||||
{" ", "Expecting value: line 1 column 3 (char 2)"},
|
||||
} {
|
||||
_, err := pyJsonLoad([]byte(c.in))
|
||||
if err == nil || err.Error() != c.msg {
|
||||
t.Errorf("pyJsonLoad(%q):\n got %v\nwant %s", c.in, err, c.msg)
|
||||
}
|
||||
}
|
||||
v, err := pyJsonLoad([]byte(`{"b": 1, "a": [2.5, 3]}`))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if got := PyDump(v); got != `{"b": 1, "a": [2.5, 3]}` {
|
||||
t.Errorf("roundtrip order: %s", got)
|
||||
}
|
||||
}
|
||||
|
||||
// TestPyParseQSQuriks — parse_qs defaults: blank values dropped, pairs
|
||||
// without '=' dropped, '+' → ' ', %XX unescaped.
|
||||
func TestPyParseQSQuriks(t *testing.T) {
|
||||
qs := pyParseQS("channel=&flag&n=8&c=%C3%A9+d&n=99")
|
||||
if _, ok := qs.Get("channel"); ok {
|
||||
t.Error("blank value must drop (keep_blank_values=False)")
|
||||
}
|
||||
if _, ok := qs.Get("flag"); ok {
|
||||
t.Error("pair without '=' must drop")
|
||||
}
|
||||
if got := qsFirst(qs, "n", "?"); got != "8" {
|
||||
t.Errorf("first value wins: got %q", got)
|
||||
}
|
||||
if got := qsFirst(qs, "c", "?"); got != "é d" {
|
||||
t.Errorf("%%XX + '+' decoding: got %q", got)
|
||||
}
|
||||
}
|
||||
|
||||
// TestChatChunksSharedID — both chunks share the base id (one uuid per
|
||||
// chat_chunks call) and the content chunk's finish_reason is null.
|
||||
func TestChatChunksSharedID(t *testing.T) {
|
||||
applyFrozenSeams("2026-10-14T08:00:00+00:00")
|
||||
result := pyDict2("model", "m", "user_text", "hi")
|
||||
chunks := chatChunks(result, "fb")
|
||||
if len(chunks) != 2 {
|
||||
t.Fatalf("chunks: %d", len(chunks))
|
||||
}
|
||||
c0 := chunks[0].(*PyDict)
|
||||
c1 := chunks[1].(*PyDict)
|
||||
id0, _ := c0.Get("id")
|
||||
id1, _ := c1.Get("id")
|
||||
if id0 != id1 || id0 != "chatcmpl-636f72652d7365727665722d" {
|
||||
t.Errorf("shared id: %v vs %v", id0, id1)
|
||||
}
|
||||
ch0 := c0.Vals["choices"].([]any)[0].(*PyDict)
|
||||
if fr, ok := ch0.Get("finish_reason"); !ok || fr != nil {
|
||||
t.Errorf("content finish_reason must be present-null: %v %v", fr, ok)
|
||||
}
|
||||
}
|
||||
|
||||
// TestCrashParityAttrError — messages as a truthy string iterates its
|
||||
// characters and crashes at m.get with the AttributeError message law.
|
||||
func TestCrashParityAttrError(t *testing.T) {
|
||||
applyFrozenSeams("2026-10-14T08:00:00+00:00")
|
||||
h := &CoreHandler{Core: &CoreServer{Operator: &SpecOperator{spec: NewPyDict()}}}
|
||||
rec := &recWriter{}
|
||||
body := `{"messages": "abc"}`
|
||||
got := func() (result any) {
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
result = pyErrString(r)
|
||||
}
|
||||
}()
|
||||
h.DoPost(rec, ChatPath, strconv.Itoa(len(body)), strings.NewReader(body))
|
||||
return nil
|
||||
}()
|
||||
if got != "'str' object has no attribute 'get'" {
|
||||
t.Errorf("crash parity: got %q", got)
|
||||
}
|
||||
}
|
||||
|
||||
// TestUnboundOperatorEnvelope — serve with no injected spec: every
|
||||
// operator call surfaces the 500 envelope error contract (the real
|
||||
// operator binding stays Python during the transition).
|
||||
func TestUnboundOperatorEnvelope(t *testing.T) {
|
||||
h := &CoreHandler{Core: &CoreServer{Operator: UnboundOperator{}}}
|
||||
rec := &recWriter{}
|
||||
h.DoGet(rec, "/v1/status")
|
||||
if rec.status != 500 {
|
||||
t.Fatalf("status: %d", rec.status)
|
||||
}
|
||||
body := rec.body.String()
|
||||
if !strings.Contains(body, `"code": "internal_error"`) ||
|
||||
!strings.Contains(body, "operator not bound") {
|
||||
t.Errorf("envelope body: %s", body)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
0
|
||||
@@ -0,0 +1 @@
|
||||
0
|
||||
@@ -0,0 +1 @@
|
||||
0
|
||||
@@ -0,0 +1 @@
|
||||
0
|
||||
@@ -0,0 +1 @@
|
||||
0
|
||||
@@ -0,0 +1 @@
|
||||
0
|
||||
@@ -0,0 +1 @@
|
||||
0
|
||||
@@ -0,0 +1 @@
|
||||
0
|
||||
@@ -0,0 +1 @@
|
||||
1
|
||||
@@ -0,0 +1 @@
|
||||
0
|
||||
@@ -0,0 +1 @@
|
||||
0
|
||||
@@ -0,0 +1,86 @@
|
||||
# core_server gap-close verification summary (dichotomy wave #2479, worktree rune-wave-df)
|
||||
|
||||
Module: `src/rune/core_server.py` (234L) → `src/go/core-server` (GO).
|
||||
Audit: **6/12 PARTIAL → 12/12 FULL** (missing list empty). Programme
|
||||
headline after this module: 107/192 FULL (56%).
|
||||
|
||||
## Ported (name-preserving, the 6 audit-missing fns)
|
||||
- `make_server` → `MakeServer(host, port, operator)` (+ `CoreServer`,
|
||||
`ServeForever` serve_forever/SIGINT mirror)
|
||||
- `log_message` → `CoreHandler.LogMessage` (no-op law)
|
||||
- `_send_json` → `CoreHandler.SendJson` (json.dumps bytes + header order)
|
||||
- `_send_ndjson` → `CoreHandler.SendNdjson`
|
||||
- `do_GET` → `CoreHandler.DoGet` (raw-URI routing incl. startswith
|
||||
conversation law, parse_qs semantics, int() ValueError → 500 envelope)
|
||||
- `do_POST` → `CoreHandler.DoPost` (approvals/chat, Python json error
|
||||
messages, `or`-laws, crash-parity AttributeError class)
|
||||
- `main` → `Main(argv)` (--host/--port argparse surface, startup line;
|
||||
the real operator binding stays Python — documented unbound default)
|
||||
|
||||
## Evidence (verification/cs-*; .log files gitignored per repo law, .exit committed)
|
||||
| Gate | Result | Evidence |
|
||||
|---|---|---|
|
||||
| gofmt -l | clean | cs-gofmt.{log,exit} |
|
||||
| go vet ./... | clean | cs-govet.{log,exit} |
|
||||
| go build ./... | clean | cs-gobuild.{log,exit} |
|
||||
| go test -v | 9/9 PASS (TestFrozenTruthReplay + 8 quirk tests) | cs-gotest.{log,exit} |
|
||||
| CI replay binary | PARITY OK: 57 cases exact-equal | cs-replay.{log,exit} |
|
||||
| freeze_truth.py --verify ×5 | 57 cases byte-identical vs live oracle | cs-freeze-verify.{log,exit} |
|
||||
| Live HTTP parity | 24/24 byte-identical (bodies, not modulo-volatile) | cs-parity.{log,exit} |
|
||||
| canary_rune_serve.py (module's own H2 battery) | 28/28 | cs-canary.{log,exit} |
|
||||
| pytest tests/test_rust_core_shims.py | 9F/12P/19S — IDENTICAL at stashed base (cs-shims-baseline.log): all 9 are the fresh-worktree sibling-kernel-build class ("kernel not built — Python oracle retired"), zero delta from this change; core_server's own tests (shapes + tui transport v1) pass | cs-pytest-shims.{log,exit} |
|
||||
| ruff (new scripts + oracle) | clean | cs-ruff.{log,exit} |
|
||||
| dichotomy_audit.py | core_server 12/12 FULL | cs-audit.{log,exit} |
|
||||
|
||||
## Both-pins statement (honest)
|
||||
core_server has NO `RUNE_CORE_SERVER_CORE` loader seam — it is a
|
||||
pure-stdlib module with no delegation machinery; the both-pins gate is
|
||||
satisfied by the oracle smoke paths above (canary sections 1–2, shims
|
||||
core_server tests) plus the --verify byte-identity gate (oracle always
|
||||
pure Python), mirroring the model_connect precedent.
|
||||
|
||||
## Oracle quirks preserved and frozen
|
||||
- json.dumps defaults on the WIRE: ", " / ": " separators + ensure_ascii
|
||||
(vendored pyjson.go PyDump/PyDict insertion-order law).
|
||||
- `uuid.uuid4().hex[:24]` + `int(time.time())` pinned via the rig
|
||||
(scripts/cs_freeze_seams) on module attrs (module-level imports —
|
||||
patchable, unlike function-local); Go seam `chunkIDFn`/`createdFn`
|
||||
(battery/replay: envelope frozen_now; live: RUNE_CS_CHUNK_HEX +
|
||||
RUNE_FROZEN_NOW).
|
||||
- `str(exc)[:200]` truncates CODEPOINTS.
|
||||
- int(s) semantics: whitespace strip, sign, underscores between digits;
|
||||
exact ValueError text.
|
||||
- CPython json scanner messages frozen verbatim (Expecting value /
|
||||
property name / ':' delimiter / Extra data / Illegal trailing comma
|
||||
obj+arr — line/column/char law).
|
||||
- parse_qs defaults: blank values dropped, no-'=' pairs dropped, '&' only,
|
||||
'+' → ' ', %XX unescape (invalid escapes literal).
|
||||
- `m.get("role")` on non-dict messages elements: string iterates
|
||||
CHARACTERS then AttributeError crash class (connection dies without a
|
||||
response — proven equal in live parity as NO_RESPONSE both sides).
|
||||
- result.get("model") or model / result.get("user_text", "") value
|
||||
pass-through: non-string truthy models (5) ride the envelope; present
|
||||
null user_text renders null; missing renders "".
|
||||
- list[:n] slice semantics for the conversation window (negative n).
|
||||
- read(Content-Length) with negative length reads to EOF (BytesIO law).
|
||||
|
||||
## Documented divergences (machinery, not module surface)
|
||||
- Garbage Content-Length over REAL HTTP: Go net/http rejects at transport
|
||||
level before the handler; Python http.server lets the handler produce
|
||||
the 400 envelope. Frozen truth covers the handler-level behavior
|
||||
byte-exactly; live parity omits the transport-level case.
|
||||
- HTTP/1.0 vs HTTP/1.1 protocol line, Server/Date headers, non-GET/POST
|
||||
501 body (Python's http.server HTML machinery): not module surface.
|
||||
- NaN/Infinity json literals and lone \uXXXX surrogates: not in the
|
||||
frozen corpus (json.dumps of NaN renders "NaN" — unportable class).
|
||||
- Python ints are unbounded; frozen corpus stays in int64.
|
||||
|
||||
## Repro
|
||||
```
|
||||
PYTHONPATH=scripts/cs_freeze_seams:src python3 scripts/gen_core_server_truth.py
|
||||
PYTHONPATH=scripts/cs_freeze_seams:src python3 scripts/freeze_truth.py \
|
||||
--verify src/go/core-server/truth.json --module rune.core_server
|
||||
cd src/go/core-server && go test && go build -o /tmp/cs . && /tmp/cs replay truth.json
|
||||
python3 scripts/check_core_server_parity.py
|
||||
python3 src/rune/canary_rune_serve.py
|
||||
```
|
||||
Reference in new issue
Block a user