tera-spatial: an NDJSON replay bridge to the TypeScript spatial environments
Tera's four (now five) spatial environments already implement a Gym-style contract with checksums, train/dev splits and a replay gate — in TypeScript, unreachable from `uv run eval`. This bridges them rather than porting them: a resident NDJSON worker over stdio drives the vendored TS closure, and no reward arithmetic exists in Python anywhere in the package, including the denominator. Correctness is replay, not assertion: every scenario x seed must round-trip through Python and replay to an identical FNV-1a-64 checksum. 32/32 do, and 52 tamper attempts are rejected, 24 of them re-sealed with Tera's own checksum so it is replay() catching divergence rather than the seal. Lives in arena rather than tera because tera has no Python toolchain. That overturns the earlier plan's claim that this workstream shares no files. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,148 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Vendor the Tera arena TypeScript closure into this package.
|
||||
|
||||
Tera is a TypeScript repository with no Python toolchain, and this package is a
|
||||
Python wheel. The bridge's rule is that a number the model is graded on is
|
||||
computed *once*, in TypeScript, and only ever transported into Python — so the
|
||||
TypeScript has to travel with the wheel rather than be resolved at runtime from
|
||||
a checkout that may not exist on the machine doing the eval.
|
||||
|
||||
What travels is the transitive import closure of `src/arena/index.ts` and
|
||||
nothing else: 23 files, no bare specifiers, no Three.js, no DOM. That closure is
|
||||
walked here rather than declared, so a new import in `tera` is picked up the
|
||||
next time somebody runs this instead of silently going missing.
|
||||
|
||||
uv run python scripts/sync_tera.py --tera ~/repos/gitea/tera
|
||||
|
||||
Writes `tera_spatial/vendor/tera/<path>` for every file in the closure and
|
||||
regenerates `tera_spatial/hashes.py`. Both are committed. `hashes.py` is what
|
||||
`test_replay.py` checks the vendored tree against, so an edit to a vendored
|
||||
file that does not come back through this script fails the gate.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import argparse
|
||||
import hashlib
|
||||
import shutil
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
sys.path.insert(0, str(Path(__file__).resolve().parent.parent))
|
||||
|
||||
from tera_spatial.closure import ENTRY, walk # noqa: E402
|
||||
|
||||
|
||||
def sha256(path: Path) -> str:
|
||||
return "sha256:" + hashlib.sha256(path.read_bytes()).hexdigest()
|
||||
|
||||
|
||||
def main(argv: list[str] | None = None) -> int:
|
||||
package = Path(__file__).resolve().parent.parent
|
||||
parser = argparse.ArgumentParser(description=__doc__)
|
||||
parser.add_argument("--tera", type=Path, default=Path.home() / "repos/gitea/tera")
|
||||
parser.add_argument("--check", action="store_true", help="verify only; write nothing")
|
||||
args = parser.parse_args(argv)
|
||||
|
||||
root = args.tera.expanduser().resolve()
|
||||
if not (root / ENTRY).is_file():
|
||||
print(f"not a tera checkout: {root}", file=sys.stderr)
|
||||
return 2
|
||||
|
||||
files, bare = walk(root)
|
||||
if bare:
|
||||
# A bare specifier means the closure needs `node_modules`, and the wheel
|
||||
# would ship a bridge that only runs next to a `npm install`.
|
||||
print("closure is not self-contained:", file=sys.stderr)
|
||||
for entry in bare:
|
||||
print(f" {entry}", file=sys.stderr)
|
||||
return 1
|
||||
|
||||
vendor = package / "tera_spatial" / "vendor" / "tera"
|
||||
if args.check:
|
||||
stale = [rel for rel in files if not (vendor / rel).is_file()]
|
||||
if stale:
|
||||
print("missing from vendor:", *stale, sep="\n ", file=sys.stderr)
|
||||
return 1
|
||||
else:
|
||||
if vendor.exists():
|
||||
shutil.rmtree(vendor)
|
||||
for rel in files:
|
||||
destination = vendor / rel
|
||||
destination.parent.mkdir(parents=True, exist_ok=True)
|
||||
shutil.copyfile(root / rel, destination)
|
||||
|
||||
digests = {rel: sha256(vendor / rel) for rel in files}
|
||||
total = sum((vendor / rel).stat().st_size for rel in files)
|
||||
aggregate = hashlib.sha256()
|
||||
for rel in files:
|
||||
aggregate.update(rel.encode())
|
||||
aggregate.update(b"\0")
|
||||
aggregate.update((vendor / rel).read_bytes())
|
||||
aggregate.update(b"\0")
|
||||
|
||||
body = _render(digests, total, "sha256:" + aggregate.hexdigest())
|
||||
target = package / "tera_spatial" / "hashes.py"
|
||||
if args.check:
|
||||
if target.read_text(encoding="utf-8") != body:
|
||||
print("hashes.py is stale; re-run scripts/sync_tera.py", file=sys.stderr)
|
||||
return 1
|
||||
print(f"vendored closure ok: {len(files)} files, {total} bytes")
|
||||
return 0
|
||||
|
||||
target.write_text(body, encoding="utf-8")
|
||||
print(f"vendored {len(files)} files ({total} bytes) from {root}")
|
||||
return 0
|
||||
|
||||
|
||||
def _render(digests: dict[str, str], total: int, aggregate: str) -> str:
|
||||
lines = [
|
||||
'"""SHA-256 of every vendored Tera source file. Generated — do not edit.',
|
||||
"",
|
||||
"Regenerate with `scripts/sync_tera.py`. `verify()` is called by the replay",
|
||||
"gate: the checksums a trace carries are only worth something if the code that",
|
||||
"produced them is the code on disk.",
|
||||
'"""',
|
||||
"",
|
||||
"from __future__ import annotations",
|
||||
"",
|
||||
"import hashlib",
|
||||
"from pathlib import Path",
|
||||
"",
|
||||
f"CLOSURE_ENTRY = {ENTRY!r}",
|
||||
f"CLOSURE_BYTES = {total}",
|
||||
f"CLOSURE_SHA256 = {aggregate!r}",
|
||||
"",
|
||||
"VENDORED_FILES: dict[str, str] = {",
|
||||
]
|
||||
lines += [f" {rel!r}: {digest!r}," for rel, digest in digests.items()]
|
||||
lines += [
|
||||
"}",
|
||||
"",
|
||||
"VENDOR_ROOT = Path(__file__).resolve().parent / \"vendor\" / \"tera\"",
|
||||
"",
|
||||
"",
|
||||
"def verify() -> list[str]:",
|
||||
' """Return a list of complaints about the vendored tree. Empty means clean."""',
|
||||
" problems: list[str] = []",
|
||||
" for relative, expected in VENDORED_FILES.items():",
|
||||
" path = VENDOR_ROOT / relative",
|
||||
" if not path.is_file():",
|
||||
" problems.append(f\"missing: {relative}\")",
|
||||
" continue",
|
||||
' actual = "sha256:" + hashlib.sha256(path.read_bytes()).hexdigest()',
|
||||
" if actual != expected:",
|
||||
' problems.append(f"modified: {relative} ({actual} != {expected})")',
|
||||
" for path in sorted(VENDOR_ROOT.rglob(\"*\")):",
|
||||
" if path.is_file():",
|
||||
" relative = path.relative_to(VENDOR_ROOT).as_posix()",
|
||||
" if relative not in VENDORED_FILES:",
|
||||
' problems.append(f"unrecorded: {relative}")',
|
||||
" return problems",
|
||||
"",
|
||||
]
|
||||
return "\n".join(lines)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
raise SystemExit(main())
|
||||
Reference in New Issue
Block a user