Log in
scripts/public_readiness.py 159 lines · 5.3 KB · blame Source
88e86b3faccd Rebuild benchmarks as a clean single-root reposi 2 months ago
1
#!/usr/bin/env python3
2
"""Run the mechanical checks for a public benchmark drop.
3
4
This script does not run benchmarks. It takes the JSONL files already intended
5
for publication and writes a small readiness bundle:
6
7
- publish_gate.txt: hard mechanical gate output
8
- benchmark_stats.md: bootstrap confidence intervals for the same JSONL files
9
- opportunities.md / opportunities.json: ranked backlog from the same results
10
- manifest.json: exact inputs, commands, exit codes, and generated paths
11
"""
12
13
from __future__ import annotations
14
15
import argparse
16
import contextlib
17
import io
18
import json
19
import subprocess
20
import sys
21
from datetime import datetime, timezone
22
from pathlib import Path
23
from typing import Any
24
25
import opportunities
26
import publish_gate
27
28
29
ROOT = Path(__file__).resolve().parents[1]
30
DEFAULT_RESULTS = ROOT / "results"
31
32
33
def parse_args(argv: list[str] | None = None) -> argparse.Namespace:
34
    parser = argparse.ArgumentParser(description=__doc__.splitlines()[0])
35
    parser.add_argument(
36
        "jsonl",
37
        nargs="*",
38
        type=Path,
39
        help="Result JSONL files to publish. Defaults to latest JSONL files in --results.",
40
    )
41
    parser.add_argument("--results", type=Path, default=DEFAULT_RESULTS)
42
    parser.add_argument(
43
        "--out-dir",
44
        type=Path,
45
        help="Directory for readiness reports. Default: <results>/public-readiness.",
46
    )
47
    parser.add_argument("--bootstrap-samples", type=int, default=2000)
48
    parser.add_argument("--confidence", type=float, default=0.95)
49
    parser.add_argument("--skip-stats", action="store_true")
50
    parser.add_argument("--skip-opportunities", action="store_true")
51
    return parser.parse_args(argv)
52
53
54
def discover_inputs(results: Path) -> list[Path]:
55
    candidates = [
56
        results / "latest.jsonl",
57
        results / "latest.mount.jsonl",
58
        results / "parallel-contention" / "latest.jsonl",
59
    ]
60
    return [path for path in candidates if path.exists()]
61
62
63
def write_text(path: Path, text: str) -> str:
64
    path.parent.mkdir(parents=True, exist_ok=True)
65
    path.write_text(text)
66
    return str(path)
67
68
69
def run_publish_gate(inputs: list[Path], out_dir: Path) -> dict[str, Any]:
70
    buf = io.StringIO()
71
    with contextlib.redirect_stdout(buf):
72
        exit_code = publish_gate.main([str(path) for path in inputs])
73
    return {
74
        "name": "publish_gate",
75
        "exit_code": exit_code,
76
        "path": write_text(out_dir / "publish_gate.txt", buf.getvalue()),
77
    }
78
79
80
def run_benchmark_stats(
81
    inputs: list[Path],
82
    out_dir: Path,
83
    bootstrap_samples: int,
84
    confidence: float,
85
) -> dict[str, Any]:
86
    command = [
87
        sys.executable,
88
        str(ROOT / "scripts" / "benchmark_stats.py"),
89
        *[str(path) for path in inputs],
90
        "--bootstrap-samples",
91
        str(bootstrap_samples),
92
        "--confidence",
93
        str(confidence),
94
    ]
95
    proc = subprocess.run(command, cwd=ROOT, text=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE, check=False)
96
    stats_path = write_text(out_dir / "benchmark_stats.md", proc.stdout)
97
    stderr_path = None
98
    if proc.stderr:
99
        stderr_path = write_text(out_dir / "benchmark_stats.stderr.txt", proc.stderr)
100
    return {
101
        "name": "benchmark_stats",
102
        "exit_code": proc.returncode,
103
        "command": command,
104
        "path": stats_path,
105
        "stderr_path": stderr_path,
106
    }
107
108
109
def run_opportunities(results: Path, inputs: list[Path], out_dir: Path) -> dict[str, Any]:
110
    report, entries = opportunities.generate_report(results, input_paths=inputs)
111
    report_path = write_text(out_dir / "opportunities.md", report)
112
    json_path = out_dir / "opportunities.json"
113
    json_path.write_text(json.dumps(entries, indent=2, sort_keys=True) + "\n")
114
    return {
115
        "name": "opportunities",
116
        "exit_code": 0,
117
        "path": str(report_path),
118
        "json_path": str(json_path),
119
        "entry_count": len(entries),
120
    }
121
122
123
def main(argv: list[str] | None = None) -> int:
124
    args = parse_args(argv)
125
    results = args.results.resolve()
126
    out_dir = (args.out_dir or (results / "public-readiness")).resolve()
127
    inputs = [path.resolve() for path in (args.jsonl or discover_inputs(results))]
128
    if not inputs:
129
        print(f"No result JSONL files found. Pass files explicitly or populate {results}.")
130
        return 2
131
132
    checks: list[dict[str, Any]] = []
133
    checks.append(run_publish_gate(inputs, out_dir))
134
    if not args.skip_stats:
135
        checks.append(run_benchmark_stats(inputs, out_dir, args.bootstrap_samples, args.confidence))
136
    if not args.skip_opportunities:
137
        checks.append(run_opportunities(results, inputs, out_dir))
138
139
    manifest = {
140
        "generated_at": datetime.now(timezone.utc).isoformat(),
141
        "results_dir": str(results),
142
        "inputs": [str(path) for path in inputs],
143
        "out_dir": str(out_dir),
144
        "checks": checks,
145
        "ready": all(check["exit_code"] == 0 for check in checks),
146
    }
147
    manifest_path = out_dir / "manifest.json"
148
    manifest_path.write_text(json.dumps(manifest, indent=2, sort_keys=True) + "\n")
149
150
    print(f"Readiness bundle: {out_dir}")
151
    print(f"Manifest: {manifest_path}")
152
    for check in checks:
153
        status = "PASS" if check["exit_code"] == 0 else "FAIL"
154
        print(f"{status} {check['name']}: {check.get('path')}")
155
    return 0 if manifest["ready"] else 1
156
157
158
if __name__ == "__main__":
159
    raise SystemExit(main())