Files
rune/scripts/check_event_envelope_parity.py
T
Nick fb1fd83466 feat(event_envelope): close PARTIAL gaps — dichotomy wave
Oracle src/rune/event_envelope.py 303 lines / 8 fns: 5/8 -> 8/8
per scripts/dichotomy_audit.py (name-preserving match). The three
audit-named gaps land as Go surface in src/go/event-bus: ToDict
(to_dict), ToCanonicalDict (to_canonical_dict, incl. the loud
RuntimeError branch for un-bridged envelopes), TestEnvelope
(test_envelope), plus the canonical-bridge classmethod CreateEnvelope
(EventEnvelope.create) they depend on: event_type follows the CHANNELS
prior vocabulary (tool -> tool_result) with the prior-or-channel-or-
source-or-unknown fallback chain.

Parity repairs found on the way (all oracle-pinned):
- classifyAuthority/classifyUrgency were word-splitting: bare 'stop'
  hit the amendment list (oracle: 'stop doing' is the multi-word
  entry; 'Stop everything!' is emergency), 'stop,' and 'unstoppable'
  misfired, and the low-urgency list matched 'when'/'you'/'can'/'no'/
  'rush' as single words (oracle: 'when you can'/'no rush'; bare
  'when' stays normal). Now \b<word>\b semantics with Unicode word
  chars ('stopé' never matches).
- pyQuoteEvent had no ensure_ascii: unicode payloads hashed with raw
  UTF-8 where the oracle hashes \uXXXX escapes — payload_hash
  diverged on every unicode payload.
- createEnvelope now derives text from payload["text"] (oracle
  signature; CLI folds --text in).
- minted ids/timestamps go through the no-clock/no-random seams
  (EE_FROZEN_NOW + per-op uuid counter) — replayable under pins,
  random uuid4-shaped ids in production.

Truth: scripts/gen_event_envelope_truth.py under the
scripts/ee_freeze_seams sitecustomize rig via the shared freeze_truth
harness (83-case envelope, frozen clock + deterministic uuid counter);
byte-identical across regens and freeze_truth --verify x2; embedded
VERBATIM in truth_envelope_test.go TestFrozenEnvelopeTruthReplay +
seam units (\b matcher, ensure_ascii escaping, event_type fallbacks,
loud bridge failure). Live parity 91/91 (scripts/
check_event_envelope_parity.py, PYTHON-json byte compare; negative
control detects divergence). gofmt/vet/go build clean; Python suite
green both pins (25 live + 25 RUNE_*_CORE=python). Dichotomy audit
5/8 PARTIAL -> 8/8 FULL.
2026-10-14 05:20:27 +01:00

266 lines
10 KiB
Python

#!/usr/bin/env python3
"""check_event_envelope_parity.py — live oracle-vs-Go parity sweep for
the event_envelope gap closure (#2479, contract factory-gap-ee).
The frozen envelope (truth_envelope.json, 83 cases) pins the surface
byte-identically. THIS script drives the LIVE Python oracle (under the
scripts/ee_freeze_seams rig: frozen clock + deterministic uuid counter)
and the Go binary (`op` mode, EE_FROZEN_NOW + per-op counter reset)
across the same branches, comparing the PYTHON-json byte form of every
result (separators ", "/": ", ensure_ascii, insertion order):
* classify_authority / classify_urgency — \b<word>\b regex semantics
(multi-word "stop doing" vs bare "stop", punctuation adjacency,
unicode word chars: "stopé" never matches, channel defaults).
* create_envelope — legacy path: auto authority/urgency, explicit
overrides, unicode + nested payloads.
* EventEnvelope.create — canonical bridge: CHANNELS prior vocabulary,
channel/source/unknown fallback chain, canonical asdict nesting.
* to_dict — 9-key legacy dict with insertion-ordered payloads.
* to_canonical_dict — schema_version-first key order,
content-addressed payload_hash (ensure_ascii hashing), and the loud
RuntimeError branch for un-bridged envelopes.
* test_envelope — the in-module self-test's captured stdout.
* CHANNELS / AUTHORITY_CLASSES — the constant tables.
Usage: PYTHONPATH=scripts/ee_freeze_seams:src python3 scripts/check_event_envelope_parity.py
[--bin src/go/event-bus/event-bus]
Exit 0 on 100% parity, 1 with the first divergences.
"""
import argparse
import json
import os
import subprocess
import sys
from pathlib import Path
REPO = Path(__file__).resolve().parent.parent
sys.path.insert(0, str(REPO / "src"))
FROZEN_NOW = "2026-10-14T11:22:33.123456+00:00"
import rune.event_envelope as ee # noqa: E402 (rig active via PYTHONPATH)
CLASSIFY_AUTHORITY = [
("hello there", "conversation"),
("Fix the failing test", "work"),
("run the suite", "work"),
("build it", "conversation"),
("test everything", "conversation"),
("create a file", "work"),
("add the guard", "work"),
("remove the shim", "work"),
("update the docs", "work"),
("change the port", "work"),
("Stop everything!", "work"),
("please cancel the order", "work"),
("abort the run", "work"),
("emergency shutdown", "work"),
("halt", "work"),
("Actually, don't modify the microkernel", "conversation"),
("dont do that", "conversation"),
("stop doing that", "work"),
("use rust instead", "work"),
("rather use go", "work"),
("pause for a bit", "work"),
("wait", "work"),
("hold", "work"),
("resume the work", "work"),
("continue the build", "work"),
("go", "work"),
("how is it going", "work"),
("what is the state", "work"),
("status?", "work"),
("progress report", "work"),
("check the log", "work"),
("hello", "work"),
("hello", "conversation"),
("hello", ""),
("stop,", "work"),
("unstoppable force", "work"),
("stopit now", "work"),
("", "work"),
("", "conversation"),
(" FIX it ", "work"),
("café stop", "work"),
("stopé now", "work"),
("Let's BUILD it, then run tests", "work"),
("what about the status, and check twice", "work"),
]
CLASSIFY_URGENCY = [
("anything", "emergency"),
("urgent please", "directive"),
("asap", "directive"),
("immediately", "conversation"),
("do it now", "directive"),
("critical issue", "conversation"),
("when you can", "directive"),
("no rush", "directive"),
("eventually", "conversation"),
("sometime later", "conversation"),
("when", "directive"),
("rush", "directive"),
("hello", "conversation"),
("", "directive"),
("NOW please", "directive"),
("no rushing", "directive"), # \b boundary: "rushing" ≠ "rush"
]
CREATE_ENVELOPE = [
("user", "conversation", {"text": "hello"}),
("user", "work", {"text": "Fix the failing test"}),
("user", "work", {"text": "Stop everything!"}),
("user", "conversation", {"text": "Actually, don't modify the microkernel"}),
("user", "work", {"text": "when you can"}),
("sensor", "sensor", {}),
("user", "work", {"text": "run it"}, "status_query", "t-42", "low"),
("user", "conversation", {"text": "hi", "extra": {"nested": [1, 2.5, None, True]}}),
("user", "work", {"text": "café résumé 🎯", "z": 1, "a": 2}),
("tool", "tool", {"result": "ok", "code": 0}),
]
CREATE = [
("user", "conversation", {"text": "hello"}),
("tool", "tool", {"x": 1}),
("user", "work", {"text": "Fix it"}),
("system", "system", {}),
("bg", "background", {}),
("sensor", "sensor", {}),
("user", "", {}),
("", "", {}),
("user", "unknown-channel", {"text": "hi"}),
]
TO_DICT_STATES = [
{"event_id": "evt_abc123def456", "timestamp": FROZEN_NOW,
"source": "user", "channel": "conversation", "authority_class": "conversation",
"task_id": None, "payload": {"text": "hi"}, "provenance": {}, "urgency": "normal"},
{"event_id": "id-2", "timestamp": "2026-10-14T00:00:00+00:00",
"source": "tool", "channel": "work", "authority_class": "directive",
"task_id": "t-9", "payload": {"n": 1.5, "b": False, "nil": None, "list": [1, "two"]},
"provenance": {"origin": "café", "chain": ["a", "b"]}, "urgency": "high"},
{"event_id": "id-3", "timestamp": FROZEN_NOW,
"source": "user", "channel": "", "authority_class": "",
"task_id": None, "payload": {}, "provenance": {"k": "v"}, "urgency": "low"},
]
TO_CANONICAL = [
("user", "conversation", {"text": "hello"}),
("tool", "tool", {"x": 1, "a": 2.0}),
("user", "work", {"text": "café résumé", "z": 1, "a": [True, None]}),
("system", "system", {}),
("bg", "background", {"nested": {"deep": [{"k": "v"}, 7]}}),
]
def run_go(binpath, env, req):
p = subprocess.run([str(binpath), "op"], input=json.dumps(req),
capture_output=True, text=True, env=env)
if p.returncode != 0:
raise RuntimeError(f"go op {req.get('op')} exited {p.returncode}: "
f"{p.stdout} {p.stderr}")
return json.loads(p.stdout)["json"]
def main():
ap = argparse.ArgumentParser()
ap.add_argument("--bin", default=str(REPO / "src/go/event-bus/event-bus"))
args = ap.parse_args()
binpath = Path(args.bin)
if not binpath.exists():
print(f"Go binary missing: {binpath} (go build -o event-bus . in src/go/event-bus)")
return 1
go_env = dict(os.environ)
go_env["EE_FROZEN_NOW"] = FROZEN_NOW
go_env.pop("EE_UUID_BASE", None)
fails = total = 0
def check(name, oracle_json, go_json):
nonlocal fails, total
total += 1
if oracle_json != go_json:
fails += 1
print(f"DIVERGE {name}:\n oracle: {oracle_json[:300]}\n"
f" go: {go_json[:300]}")
# classify — public fns (no mints; live semantics)
for text, channel in CLASSIFY_AUTHORITY:
check(f"classify_authority:{text[:30]!r}",
json.dumps(ee.classify_authority(text, channel)),
run_go(binpath, go_env, {"op": "classify_authority",
"text": text, "channel": channel}))
for text, authority in CLASSIFY_URGENCY:
check(f"classify_urgency:{text[:30]!r}",
json.dumps(ee.classify_urgency(text, authority)),
run_go(binpath, go_env, {"op": "classify_urgency",
"text": text, "authority_class": authority}))
# create_envelope — rig seam (per-call mint reset) vs op (per-op reset)
for row in CREATE_ENVELOPE:
source, channel, payload = row[0], row[1], row[2]
req = {"op": "create_envelope", "source": source, "channel": channel,
"payload": payload}
if len(row) > 3:
req["authority_class"] = row[3]
if len(row) > 4:
req["task_id"] = row[4]
if len(row) > 5:
req["urgency"] = row[5]
check(f"create_envelope:{source}/{channel}/{str(payload)[:30]}",
json.dumps(ee.ee_create_envelope(*row)),
run_go(binpath, go_env, req))
# EventEnvelope.create — canonical bridge
for source, channel, payload in CREATE:
check(f"create:{source}/{channel}",
json.dumps(ee.ee_create(source, channel, payload)),
run_go(binpath, go_env, {"op": "create", "source": source,
"channel": channel, "payload": payload}))
# to_dict — explicit states (insertion-ordered payloads byte-compared)
for state in TO_DICT_STATES:
full = dict(state)
full["_canonical"] = None
check(f"to_dict:{state['event_id']}",
json.dumps(ee.ee_to_dict(full)),
run_go(binpath, go_env, {"op": "to_dict", "state": full}))
# to_canonical_dict — bridged
for source, channel, payload in TO_CANONICAL:
check(f"to_canonical_dict:{source}/{channel}/{str(payload)[:30]}",
json.dumps(ee.ee_to_canonical_dict(source, channel, payload)),
run_go(binpath, go_env, {"op": "to_canonical_dict", "source": source,
"channel": channel, "payload": payload}))
# to_canonical_dict — unbridged loud failure
state = dict(TO_DICT_STATES[0])
state["_canonical"] = None
check("to_canonical_dict:unbridged",
json.dumps(ee.ee_to_canonical_dict_unbridged(state)),
run_go(binpath, go_env, {"op": "to_canonical_dict_unbridged",
"state": state}))
# test_envelope — captured stdout
check("test_envelope",
json.dumps(ee.ee_test_envelope()),
run_go(binpath, go_env, {"op": "test_envelope"}))
# constant tables
check("channels",
json.dumps(ee.ee_channels()),
run_go(binpath, go_env, {"op": "channels"}))
check("authority_classes",
json.dumps(ee.ee_authority_classes()),
run_go(binpath, go_env, {"op": "authority_classes"}))
print(f"parity: {total - fails}/{total} match")
return 1 if fails else 0
if __name__ == "__main__":
sys.exit(main())