#!/usr/bin/env python3 """Archive stopped TTS benchmark runs, verifying their original launch bytes. Usage: python3 docs/evidence/tts-lag-2026-10-13/archive.py a01 a02 Append a later stopped run by supplying its tag. Existing run files are immutable. This script neither launches an engine nor contacts a speech service. """ import argparse import base64 from collections import Counter import gzip import hashlib import io import json from pathlib import Path import re import sys import wave HERE = Path(__file__).resolve().parent ROOT = HERE.parents[2] sys.path.insert(0, str(ROOT / "tools")) from native_provenance import archive_source, encoded, read_launch from native_report import analyze RELEVANT = [ "addons/core/Scripts/Game/ERR/00_Core/ERR_WelcomeVoice.c", "addons/core/Scripts/Game/ERR/00_Core/ERR_SpeechService.c", "addons/core/Scripts/Game/ERR/00_Core/ERR_SpeechMenu.c", "addons/core/Scripts/Game/ERR/00_Core/ERR_AdminMenu.c", "addons/core/Scripts/Game/ERR/00_Core/ERR_Settings.c", "addons/core/Scripts/Game/ERR/10_AI/ERR_SoldierSpeech.c", "addons/core/Scripts/Game/ERR/00_Core/ERR_ZSpeechBenchmark.c", "addons/core/Configs/ERR/Tests/LegacySites.conf", ] CLIENT_EVIDENCE = ( "client-events.txt", "log-findings.json", "admin-warning-fix.patch", "admin-warning-validation.txt", ) def sha256(data): return hashlib.sha256(data).hexdigest() def write_new(path, data): path.parent.mkdir(parents=True, exist_ok=True) with path.open("xb") as stream: stream.write(data) assert path.read_bytes() == data def copy(source, destination): assert source.is_file() and not source.is_symlink(), source write_new(destination, source.read_bytes()) def write_or_verify(path, data): if path.exists(): assert path.read_bytes() == data, f"Existing evidence differs: {path}" else: write_new(path, data) def events(text): records = [] for number, line in enumerate(text.splitlines(), 1): match = re.search(r"ERR TTS ([A-Z ]+): (.*)", line) if not match: continue values = {} for key, value in re.findall(r"(\w+)=([^ ]+)", match[2]): try: values[key] = int(value) except ValueError: try: values[key] = float(value) except ValueError: values[key] = value records.append({"console_line": number, "timestamp": line[:12], "kind": match[1], "values": values, "raw": line}) return records def archive(tag): profile = Path("/tmp/erri-tts-bench-run-" + tag) source = Path("/tmp/erri-tts-bench-source-" + tag) sessions = list((profile / "logs").glob("logs_*")) assert len(sessions) == 1, (profile, sessions) session = sessions[0] manifest, metadata, runner = read_launch(session) assert runner["cleanup_confirmed"] is True assert runner["cleanup_report"]["idle_verified"] is True assert runner["external_addons_unchanged_after_run"] is True manifest_bytes, provenance, launched = archive_source(session, source) assert launched and metadata["source_root"] == str(source) for name in RELEVANT: assert sha256((source / name).read_bytes()) == manifest[name], name destination = HERE / tag destination.mkdir(exist_ok=False) for name in ("console.log", "script.log", "error.log", "runner.json", "external-addons-inputs.json", "headless-launch.log"): copy(session / name, destination / (name + ".txt")) for name in ("launch-source-sha256.json", "launch-source.json"): copy(session / name, destination / name) write_new(destination / "source-sha256.json", manifest_bytes) write_new(destination / "source-provenance.json", encoded(provenance)) source_matches = {} for name in RELEVANT: copy(source / name, destination / "source" / name) current = ROOT / name source_matches[name] = { "launch_sha256": manifest[name], "archived_sha256": sha256((destination / "source" / name).read_bytes()), "repository_exists_at_archive": current.is_file(), "repository_matches_launch_at_archive": current.is_file() and sha256(current.read_bytes()) == manifest[name], } write_new(destination / "relevant-source-matches.json", encoded(source_matches)) for name in ("samples.json", "production-sources.json"): if (profile / name).exists(): copy(profile / name, destination / name) runner_output = Path("/tmp/erri-tts-bench-" + tag + "-runner.log") if runner_output.exists(): copy(runner_output, destination / "runner-output.log.txt") samples = {} for path in sorted((profile / "profile").glob("tts-*.b64")): raw = path.read_bytes() pcm = base64.b64decode(raw, validate=True) seconds = int(path.stem.split("-")[1]) with wave.open(io.BytesIO(pcm), "rb") as wav: assert wav.getnchannels() == 1 and wav.getsampwidth() == 2 assert wav.getframerate() == 24000 and wav.getnframes() == seconds * 24000 assert not any(wav.readframes(wav.getnframes())), "Fixture must contain synthetic silence only" write_new(destination / "inputs" / (path.name + ".gz"), gzip.compress(raw, mtime=0)) assert gzip.decompress((destination / "inputs" / (path.name + ".gz")).read_bytes()) == raw samples[str(seconds)] = { "base64_bytes": len(raw), "base64_sha256": sha256(raw), "wav_bytes": len(pcm), "wav_sha256": sha256(pcm), "format": "RIFF PCM mono 24000 Hz 16 bit", "synthetic_silence": True, } outputs = [path.with_suffix(".wav"), path.with_name("tts-new-output-" + str(seconds) + ".wav")] for output in outputs: if not output.exists(): continue assert output.read_bytes() == pcm, f"Decoded WAV differs from input: {output}" samples[str(seconds)].setdefault("native_outputs", {})[output.name] = { "matches_input": True, "sha256": sha256(output.read_bytes()), } write_new(destination / "outputs" / (output.name + ".gz"), gzip.compress(output.read_bytes(), mtime=0)) write_new(destination / "input-output-sha256.json", encoded(samples)) text = (session / "console.log").read_text(errors="replace") report = analyze(text, str((destination / "console.log.txt").relative_to(ROOT))) report["scope"] = "Local Workbench script timing with synthetic PCM silence; no service contact, network latency, audible quality or multiplayer qualification. 1 ms timer resolution." write_new(destination / "native-report.json", encoded(report)) write_new(destination / "timings.json", encoded(events(text))) hashes = {str(path.relative_to(destination)): sha256(path.read_bytes()) for path in sorted(destination.rglob("*")) if path.is_file()} write_new(destination / "artifact-sha256.json", encoded(hashes)) print(f"Archived {tag}: {runner['result']}, {len(events(text))} timing records, cleanup verified") def summarize(): runs = {} manifests = {} errors = {} for folder in sorted(HERE.glob("a[0-9][0-9]")): if not folder.is_dir(): continue manifest, metadata, runner = read_launch(folder, "runner.json.txt", "console.log.txt") report = json.loads((folder / "native-report.json").read_text()) hashes = json.loads((folder / "artifact-sha256.json").read_text()) assert all(sha256((folder / name).read_bytes()) == digest for name, digest in hashes.items()) manifests[folder.name] = manifest errors[folder.name] = Counter(re.sub(r"\s+", " ", re.sub(r"^\d\d:\d\d:\d\d\.\d+\s+", "", line)) for line in report["engine_errors"]) runs[folder.name] = { "runner_result": runner["result"], "cleanup_confirmed": runner["cleanup_confirmed"], "external_addons_unchanged_after_run": runner["external_addons_unchanged_after_run"], "assertions_passed": report["assertions_passed"], "smoke_completions": report["smoke_completions"], "script_or_runtime_failure_count": len(report["script_or_runtime_failures"]), "engine_error_count": len(report["engine_errors"]), "timings": json.loads((folder / "timings.json").read_text()), } starts = [event for event in runs[folder.name]["timings"] if event["kind"] == "NEW START"] if starts: def milliseconds(timestamp): hours, minutes, seconds = timestamp.split(":") return round((int(hours) * 3600 + int(minutes) * 60 + float(seconds)) * 1000) started = milliseconds(starts[0]["timestamp"]) runs[folder.name]["queued_batch_playback_delay_ms"] = { str(event["values"]["seconds"]): (milliseconds(event["timestamp"]) - started) % 86400000 for event in runs[folder.name]["timings"] if event["kind"] == "NEW PLAY" } changes = {} error_changes = {} tags = sorted(manifests) for previous, current in zip(tags, tags[1:]): one, two = manifests[previous], manifests[current] changes[previous + "_to_" + current] = sorted(name for name in set(one) | set(two) if one.get(name) != two.get(name)) error_changes[previous + "_to_" + current] = { "new_signatures": sorted(set(errors[current]) - set(errors[previous])), "removed_signatures": sorted(set(errors[previous]) - set(errors[current])), "additional_occurrences": dict(errors[current] - errors[previous]), } report = { "scope": "Timing measured inside native Enforce script callbacks. This is component preparation cost, not a rendered-frame percentile or network benchmark.", "runs": runs, "changed_launch_sources": changes, "engine_error_comparison": error_changes, "client_log_evidence": "client/log-findings.json", "client_limitations": "Download-to-play gaps include network and preparation; no per-frame client telemetry was retained, so those gaps alone do not establish render hitches.", "audio_limitations": "Synthetic silence is terminated immediately in baseline runs; timing does not qualify audible speech, an entire utterance, or actual multiplayer streaming.", "queued_measurement_limitations": "Queued fixtures inject local payloads before timing. Shared-batch playback waits are not single-clip or network latency. maxPrepareMs covers only one preparation call; maxScriptUpdateMs and maxFrameGapMs also include other active mission work, so the change does not prove all game frames are hitch-free.", "provenance": "read_launch validates original capture, stopped console hash and unchanged source manifest. archive_source verifies full frozen addon bytes. Per-run artifact hashes bind preserved evidence; runner confirms cleanup and unchanged external addon inputs.", } (HERE / "report.json").write_bytes(encoded(report)) def main(): parser = argparse.ArgumentParser(description=__doc__) parser.add_argument("runs", nargs="*", help="Stopped benchmark tags, e.g. a01 a02 a03") args = parser.parse_args() for tag in args.runs: if not re.fullmatch(r"a[0-9]{2}", tag): parser.error("Expected a run tag such as a03") archive(tag) for name in CLIENT_EVIDENCE: write_or_verify(HERE / "client" / name, (ROOT / ".local/tts-lag-20261013" / name).read_bytes()) summarize() if __name__ == "__main__": main()