arborist/bench/batteries/runner.py
russell@unturf.com 6e20c792c4
bench: 5R battery — closes ticket #000021 (15-sub-battery suite complete)
Phase 2 of #000021. React/Rearrange/Restore/Replicate/Resonate over
the workspace surface — selfmodel_records (#000014) + memory_records
(#000017), both landed earlier today. Closes the gap that gated 5R
since the substrate work shipped.

Sub-battery semantics (per SQD whitepaper §9.3 + ticket #000021 §4.2):

- React: incorporate new fact/constraint. Workspace = (snapshot_t0,
  snapshot_t1, expected_delta). Pass = added_facts present + removed_facts
  absent in t+1.
- Rearrange: restructure without semantic shift. Re-canonicalize
  different surface forms through a named π*; pass = bytes match
  expected_equivalent flag. Tests the order-invariance contracts in
  SelfModel (capability_claim_hashes sorted) and Memory (branches
  sorted by branch_id).
- Restore: retrieve prior fact. Workspace = (history[], current_facts[]).
  Pass = fact in current OR any historical snapshot.
- Replicate: independent canonical encodings via π*. Same input run
  N times must yield byte-equal output. Tests determinism contract.
- Resonate: variance across N runs. Deterministic π*'s yield
  distinct=1; expected_max_distinct=1 enforces zero-variance contract.

Surface:

- bench/batteries/b_5r.py (5 deterministic runners; no LLM-as-judge)
- bench/fixtures/5r/{react,rearrange,restore,replicate,resonate}-v1.jsonl
  (30 each = 150 new fixtures)
- runner.py registers 5r in _BATTERIES + _DEFAULT_FIXTURES
- Makefile: bench-5r + bench-suite (5S+5T+5F+5R aggregate)

Final tally:
  5S  syntax/semantics/syllogism/synthesis/semiotics       108
  5T  transfer/transfer-learning/triangulation/...          154
  5F  function/finetuning/falsification/...                  50
  5R  react/rearrange/restore/replicate/resonate            150
  TOTAL: 462 fixtures across 21 sub-batteries — 100% pass.

Tests: 6 new in tests/test_bench_batteries.py + adjustment to
test_session_integration.py for the 312→462 count + 5R sub-battery
presence assertion. Full suite: 1192 passed, 36 skipped.

Closes #000021. Phase 3 (external-corpus expansion) remains open
under the ticket but does not gate closure — the complete
Dav1DPrometheus surface is now executable infrastructure.
2026-05-08 08:06:24 -04:00

141 lines
5.2 KiB
Python

"""CLI entry: ``python -m bench.batteries.runner``.
Runs one or more battery sub-batteries against their fixture files
and emits a JSON result on stdout (or to ``--out`` if provided).
Usage examples
--------------
::
# Run 5S Syntax + Semantics with the seed fixtures.
python -m bench.batteries.runner \\
--battery 5s --sub syntax --fixtures bench/fixtures/5s/syntax-v1.jsonl
python -m bench.batteries.runner \\
--battery 5s --sub semantics \\
--fixtures bench/fixtures/5s/semantics-v1.jsonl
# Run 5T Transfer.
python -m bench.batteries.runner \\
--battery 5t --sub transfer \\
--fixtures bench/fixtures/5t/transfer-v1.jsonl
# All Phase-1a fixtures, written to a results file.
python -m bench.batteries.runner --all --out bench/results/run.json
"""
from __future__ import annotations
import argparse
import json
import sys
from dataclasses import asdict
from pathlib import Path
from typing import Optional
from bench.batteries import b_5f, b_5r, b_5s, b_5t
from bench.batteries.base import BatteryResult
_BATTERIES = {
"5s": b_5s.SUB_BATTERIES,
"5t": b_5t.SUB_BATTERIES,
"5f": b_5f.SUB_BATTERIES,
"5r": b_5r.SUB_BATTERIES,
}
_DEFAULT_FIXTURES = {
# 5S — Phase 1a
("5s", "syntax"): "bench/fixtures/5s/syntax-v1.jsonl",
("5s", "semantics"): "bench/fixtures/5s/semantics-v1.jsonl",
# 5S — Phase 1b (ticket #000023)
("5s", "syllogism"): "bench/fixtures/5s/syllogism-v1.jsonl",
("5s", "synthesis"): "bench/fixtures/5s/synthesis-v1.jsonl",
("5s", "semiotics"): "bench/fixtures/5s/semiotics-v1.jsonl",
# 5T — Phase 1a (legacy SQD vocabulary)
("5t", "transfer"): "bench/fixtures/5t/transfer-v1.jsonl",
# 5T — Phase 1b (Dav1DPrometheus vocabulary, ticket #000024)
("5t", "transfer-learning"): "bench/fixtures/5t/transfer-learning-v2.jsonl",
("5t", "triangulation"): "bench/fixtures/5t/triangulation-v1.jsonl",
("5t", "truthtables"): "bench/fixtures/5t/truthtables-v1.jsonl",
("5t", "transitivity"): "bench/fixtures/5t/transitivity-v1.jsonl",
("5t", "time"): "bench/fixtures/5t/time-v1.jsonl",
# 5F — Phase 1a (ticket #000025)
("5f", "function"): "bench/fixtures/5f/function-v1.jsonl",
("5f", "finetuning"): "bench/fixtures/5f/finetuning-v1.jsonl",
("5f", "falsification"): "bench/fixtures/5f/falsification-v1.jsonl",
("5f", "formulate"): "bench/fixtures/5f/formulate-v1.jsonl",
("5f", "feedback-loop"): "bench/fixtures/5f/feedback-loop-v1.jsonl",
# 5R — Phase 2 of #000021 (ticket #000021 §7 Phase 2; depends on
# SelfModel + memory_root, both landed)
("5r", "react"): "bench/fixtures/5r/react-v1.jsonl",
("5r", "rearrange"): "bench/fixtures/5r/rearrange-v1.jsonl",
("5r", "restore"): "bench/fixtures/5r/restore-v1.jsonl",
("5r", "replicate"): "bench/fixtures/5r/replicate-v1.jsonl",
("5r", "resonate"): "bench/fixtures/5r/resonate-v1.jsonl",
}
def _run_one(battery: str, sub: str, fixtures: Path) -> BatteryResult:
if battery not in _BATTERIES:
raise SystemExit(f"unknown battery {battery!r}")
if sub not in _BATTERIES[battery]:
raise SystemExit(f"unknown sub-battery {sub!r} for {battery}")
return _BATTERIES[battery][sub](fixtures)
def _emit(results: list[BatteryResult], out: Optional[str]) -> None:
payload = {
"schema_version": "bench-result-v1",
"results": [asdict(r) for r in results],
}
encoded = json.dumps(payload, indent=2, ensure_ascii=False, default=str)
if out:
Path(out).parent.mkdir(parents=True, exist_ok=True)
Path(out).write_text(encoded + "\n", encoding="utf-8")
print(f"wrote {out}")
else:
print(encoded)
def main(argv: Optional[list[str]] = None) -> int:
p = argparse.ArgumentParser(description=__doc__)
p.add_argument("--battery", choices=sorted(_BATTERIES), default=None)
p.add_argument("--sub", default=None, help="sub-battery name")
p.add_argument("--fixtures", default=None, help="fixture JSONL path")
p.add_argument(
"--all",
dest="run_all",
action="store_true",
help="run all Phase-1a sub-batteries with default fixtures",
)
p.add_argument("--out", default=None, help="write JSON to this path")
args = p.parse_args(argv)
results: list[BatteryResult] = []
if args.run_all:
for (battery, sub), fixture_path in _DEFAULT_FIXTURES.items():
results.append(_run_one(battery, sub, Path(fixture_path)))
else:
if not args.battery or not args.sub:
p.error("--battery and --sub are required unless --all is given")
if args.fixtures:
fixtures = Path(args.fixtures)
else:
key = (args.battery, args.sub)
if key not in _DEFAULT_FIXTURES:
p.error(
f"no default fixture for ({args.battery},{args.sub}); "
"pass --fixtures explicitly"
)
fixtures = Path(_DEFAULT_FIXTURES[key])
results.append(_run_one(args.battery, args.sub, fixtures))
_emit(results, args.out)
failed = sum(r.fail_count for r in results)
return 1 if failed else 0
if __name__ == "__main__": # pragma: no cover
sys.exit(main())