mirror of
https://github.com/abhigyanpatwari/GitNexus.git
synced 2026-10-28 06:22:22 +00:00
* feat(eval): measure PR cost before merge Reuse authenticated historical MCP measurements for one pinned PR preview. Keep the protected main evaluator and publish separate evidence that cannot authorize a release. * fix(eval): gate PR cost runs on a label Require a repository writer label event and retain the opt-in label and exact PR head before dispatch and paid sessions. Keep the request handler on trusted base code and execute evaluation on main. * ci: document trusted cost label trigger * fix(eval): restrict previews to maintainer labels Remove manual PR preview inputs and authorize Maintain/Admin roles before calling the protected reusable evaluator. Compare workflow measurement settings with historical evidence instead of exempting the entire workflow. --------- Co-authored-by: Gergo Magyar <gergomagyar0@gmail.com>
509 lines
22 KiB
Python
509 lines
22 KiB
Python
"""Compare a pinned, unmerged PR's MCP sessions with authenticated past measurements.
|
|
|
|
The trusted main workflow owns this driver. Candidate code runs only through
|
|
the existing contained runtime builder and benchmark. Preview artifacts never
|
|
use the release-evidence schema or artifact name.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
import argparse
|
|
import json
|
|
import math
|
|
import re
|
|
import statistics
|
|
import subprocess
|
|
import tempfile
|
|
from datetime import UTC, datetime
|
|
from pathlib import Path
|
|
from typing import Any
|
|
|
|
SHA = re.compile(r"[0-9a-f]{40}")
|
|
REPO = re.compile(r"[\w.-]+/[\w.-]+")
|
|
COST_LABEL = "run-cost-evaluation"
|
|
# These additions orchestrate the preview; they do not change the measured
|
|
# runner, task/oracle bytes, dependency lock or pinned agent CLI.
|
|
PREVIEW_PATHS = {
|
|
"eval/workflow_bench/pr_evaluation.py",
|
|
"eval/tests/test_pr_evaluation.py",
|
|
"eval/tests/test_ec2_workflows.py",
|
|
"eval/workflow_bench/README.md",
|
|
}
|
|
EVALUATION_WORKFLOW = ".github/workflows/release-evaluation.yml"
|
|
MEASUREMENT_PATHS = ("eval/", ".github/claude-canary-runtime/", EVALUATION_WORKFLOW)
|
|
|
|
|
|
def _gh(arguments: list[str]) -> str:
|
|
try:
|
|
return subprocess.run(["gh", *arguments], check=True, capture_output=True, text=True, timeout=120).stdout
|
|
except (subprocess.CalledProcessError, subprocess.TimeoutExpired):
|
|
raise ValueError("GitHub evidence request failed") from None
|
|
|
|
|
|
def _gh_json(endpoint: str) -> dict[str, Any]:
|
|
return json.loads(_gh(["api", endpoint]))
|
|
|
|
|
|
def resolve_pr(repo: str, number: int, expected_sha: str) -> str:
|
|
if not REPO.fullmatch(repo) or type(number) is not int or number < 1 or not SHA.fullmatch(expected_sha):
|
|
raise ValueError("expected a repository, positive PR number and full PR head SHA")
|
|
pr = _gh_json(f"repos/{repo}/pulls/{number}")
|
|
if (
|
|
pr.get("state") != "open"
|
|
or pr.get("head", {}).get("sha") != expected_sha
|
|
or pr.get("base", {}).get("ref") != "main"
|
|
or pr.get("base", {}).get("repo", {}).get("full_name") != repo
|
|
):
|
|
raise ValueError("PR is closed, moved from the requested SHA, or targets a different repository/branch")
|
|
if COST_LABEL not in {label.get("name") for label in pr.get("labels", [])}:
|
|
raise ValueError(f"PR must retain the {COST_LABEL} label to enable a cost comparison")
|
|
return expected_sha
|
|
|
|
|
|
def dispatch_labeled_pr(repo: str, event: dict[str, Any]) -> int:
|
|
"""Authorize a label request and select its baseline for the reusable evaluator."""
|
|
if (
|
|
not REPO.fullmatch(repo)
|
|
or event.get("repository", {}).get("full_name") != repo
|
|
or event.get("action") != "labeled"
|
|
or event.get("label", {}).get("name") != COST_LABEL
|
|
):
|
|
raise ValueError("expected a cost-evaluation label event for this repository")
|
|
actor = event.get("sender", {}).get("login", "")
|
|
if not re.fullmatch(r"[\w-]+(?:\[bot\])?", actor):
|
|
raise ValueError("invalid label actor")
|
|
permission = _gh_json(f"repos/{repo}/collaborators/{actor}/permission")
|
|
if permission.get("role_name") not in ("admin", "maintain"):
|
|
raise ValueError("only repository maintainers or administrators may request paid evaluations")
|
|
pr = event.get("pull_request", {})
|
|
number = event.get("number")
|
|
sha = resolve_pr(repo, number, pr.get("head", {}).get("sha", ""))
|
|
# Previews and runner-only probes are not historical release measurements.
|
|
# The evaluator authenticates the chosen artifact's contents before spend.
|
|
runs = _gh_json(
|
|
f"repos/{repo}/actions/workflows/release-evaluation.yml/runs?branch=main&status=success&per_page=100"
|
|
)
|
|
for run in runs.get("workflow_runs", []):
|
|
if (
|
|
run.get("conclusion") != "success"
|
|
or run.get("head_branch") != "main"
|
|
or run.get("event") not in ("schedule", "workflow_dispatch")
|
|
or run.get("head_repository", {}).get("full_name") != repo
|
|
or type(run.get("id")) is not int
|
|
or run["id"] < 1
|
|
or not SHA.fullmatch(run.get("head_sha", ""))
|
|
):
|
|
continue
|
|
artifacts = _gh_json(f"repos/{repo}/actions/runs/{run['id']}/artifacts?per_page=100")
|
|
if artifacts.get("total_count", 0) > 100:
|
|
raise ValueError("historical artifact list is truncated")
|
|
matches = [
|
|
item
|
|
for item in artifacts.get("artifacts", [])
|
|
if isinstance(item.get("name"), str)
|
|
and re.fullmatch(r"release-agent-evaluation-[0-9a-f]{40}", item["name"])
|
|
and item.get("expired") is False
|
|
]
|
|
if len(matches) != 1:
|
|
continue
|
|
resolve_pr(repo, number, sha)
|
|
return run["id"]
|
|
raise ValueError("no full successful main Release evaluation found in the latest 100 runs")
|
|
|
|
|
|
def measurement_protocol(source: str) -> dict[str, Any]:
|
|
"""Keep setup and paid execution identical, allowing only the preview routing."""
|
|
import yaml
|
|
|
|
job = yaml.safe_load(source)["jobs"]["evaluate"]
|
|
protocol = {key: job[key] for key in ("runs-on", "environment", "timeout-minutes")}
|
|
protocol["env"] = {
|
|
key: value for key, value in job["env"].items() if key not in {"INPUT_PR", "INPUT_PR_SHA", "COMPARISON_RUN_ID"}
|
|
}
|
|
steps = []
|
|
if sum(step.get("run", "").count("-m workflow_bench.runner") for step in job["steps"]) != 1:
|
|
raise ValueError("expected one paid measurement step")
|
|
for step in job["steps"]:
|
|
if step.get("id") == "resolve":
|
|
continue # Runtime selection is intentionally different for an unmerged PR.
|
|
if step.get("name") in {
|
|
"Authenticate historical PR comparison evidence",
|
|
"Require historical task dependencies before paid PR sessions",
|
|
"Recheck PR opt-in and pinned head immediately before paid sessions",
|
|
}:
|
|
if step.get("if") != "inputs.candidate_pr != ''":
|
|
raise ValueError("preview preflight must be conditional on the PR request")
|
|
continue
|
|
normalized = dict(step)
|
|
if step.get("with", {}).get("path") == "stable" and step.get("if") == "inputs.candidate_pr == ''":
|
|
normalized.pop("if")
|
|
script = step.get("run", "")
|
|
# Only the exact reviewed preview selectors may differ. Flags, setup
|
|
# actions, pins, environment, timeouts, ordering and other shell stay.
|
|
for variable in ("checkout", "runtime"):
|
|
script = script.replace(f' if [[ -n "$INPUT_PR" && "${variable}" == stable ]]; then continue; fi\n', "")
|
|
script = script.replace(
|
|
'arms=(baseline_nomcp baseline)\nif [[ -n "$INPUT_PR" ]]; then arms=(baseline); fi\n', ""
|
|
)
|
|
script = script.replace('--arms "${arms[@]}"', "--arms baseline_nomcp baseline")
|
|
if "run" in step:
|
|
normalized["run"] = script
|
|
steps.append(normalized)
|
|
if "-m workflow_bench.runner" in script:
|
|
break
|
|
protocol["steps"] = steps
|
|
return protocol
|
|
|
|
|
|
def require_comparable_harness(repo: str, previous_sha: str, harness_sha: str) -> None:
|
|
comparison = _gh_json(f"repos/{repo}/compare/{previous_sha}...{harness_sha}")
|
|
files = comparison.get("files")
|
|
if comparison.get("status") not in ("identical", "ahead") or not isinstance(files, list) or len(files) >= 300:
|
|
raise ValueError("cannot prove historical measurement inputs are comparable")
|
|
workflow_changed = False
|
|
for entry in files:
|
|
if not isinstance(entry, dict) or not isinstance(entry.get("filename"), str):
|
|
raise TypeError("malformed harness comparison")
|
|
for name in (entry["filename"], entry.get("previous_filename")):
|
|
if name == EVALUATION_WORKFLOW:
|
|
if entry["filename"] != name or entry.get("previous_filename", name) != name:
|
|
raise ValueError("evaluation workflow was renamed; measure a fresh baseline")
|
|
workflow_changed = True
|
|
continue
|
|
if isinstance(name, str) and name.startswith(MEASUREMENT_PATHS) and name not in PREVIEW_PATHS:
|
|
raise ValueError(
|
|
"historical runner, tasks, dependencies or agent CLI changed; measure a fresh baseline"
|
|
)
|
|
if workflow_changed:
|
|
protocols = [
|
|
measurement_protocol(
|
|
_gh(
|
|
[
|
|
"api",
|
|
f"repos/{repo}/contents/{EVALUATION_WORKFLOW}?ref={sha}",
|
|
"-H",
|
|
"Accept: application/vnd.github.raw+json",
|
|
]
|
|
)
|
|
)
|
|
for sha in (previous_sha, harness_sha)
|
|
]
|
|
if protocols[0] != protocols[1]:
|
|
raise ValueError("historical workflow measurement protocol changed; measure a fresh baseline")
|
|
|
|
|
|
def fetch_baseline(repo: str, run_id: int, harness_sha: str, model: str, effort: str) -> dict[str, Any]:
|
|
from .release_evidence import eligible_runs
|
|
from .release_report import suite_binding, validate_report
|
|
|
|
if not REPO.fullmatch(repo) or run_id < 1 or not SHA.fullmatch(harness_sha):
|
|
raise ValueError("invalid historical run request")
|
|
run = _gh_json(f"repos/{repo}/actions/runs/{run_id}")
|
|
if (
|
|
run.get("id") != run_id
|
|
or run.get("path") != ".github/workflows/release-evaluation.yml"
|
|
or not eligible_runs({"workflow_runs": [run]}, repo)
|
|
):
|
|
raise ValueError("baseline must be a successful trusted main Release evaluation run")
|
|
artifacts = _gh_json(f"repos/{repo}/actions/runs/{run_id}/artifacts?per_page=100")
|
|
if artifacts.get("total_count", 0) > 100:
|
|
raise ValueError("historical artifact list is truncated")
|
|
matches = [
|
|
item
|
|
for item in artifacts.get("artifacts", [])
|
|
if isinstance(item.get("name"), str)
|
|
and re.fullmatch(r"release-agent-evaluation-[0-9a-f]{40}", item["name"])
|
|
and item.get("expired") is False
|
|
]
|
|
if len(matches) != 1:
|
|
raise ValueError("expected exactly one live release measurement artifact")
|
|
artifact = matches[0]
|
|
with tempfile.TemporaryDirectory(prefix="pr-evaluation-baseline-") as directory:
|
|
_gh(["run", "download", str(run_id), "--repo", repo, "--name", artifact["name"], "--dir", directory])
|
|
source = Path(directory) / "agent-evaluation.json"
|
|
if not source.is_file() or source.is_symlink() or source.stat().st_size > 2 * 1024 * 1024:
|
|
raise ValueError("invalid historical report artifact")
|
|
report = json.loads(source.read_text())
|
|
_, digest, pins = suite_binding()
|
|
runtime_sha = artifact["name"].removeprefix("release-agent-evaluation-")
|
|
validate_report(report, runtime_sha=runtime_sha, task_set_digest=digest, task_pins=pins)
|
|
if (
|
|
report["harness_sha"] != run["head_sha"]
|
|
or report["model"] != model
|
|
or report["effort"] != effort
|
|
or report["runs"] != 3
|
|
):
|
|
raise ValueError("historical harness, model, reasoning effort or repetition count does not match")
|
|
require_comparable_harness(repo, run["head_sha"], harness_sha)
|
|
return report
|
|
|
|
|
|
def _number(value: Any) -> bool:
|
|
try:
|
|
return type(value) in (int, float) and value >= 0 and math.isfinite(value)
|
|
except OverflowError:
|
|
return False
|
|
|
|
|
|
def check_dependency_bindings(bindings: list[dict[str, Any]], previous: dict[str, Any]) -> None:
|
|
from .task_assets import DEPENDENCY_CONTENT_BINDING_FIELD
|
|
|
|
if len(bindings) != len(previous["tasks"]) or {entry["id"] for entry in bindings} != set(previous["tasks"]):
|
|
raise ValueError("prepared task inventory differs from the historical run")
|
|
for entry in bindings:
|
|
task = entry["id"]
|
|
pin = previous["tasks"][task]
|
|
expected = {
|
|
row[DEPENDENCY_CONTENT_BINDING_FIELD]
|
|
for row in previous["per_run"]
|
|
if row["task"] == task and row["arm"] == "baseline"
|
|
}
|
|
if (
|
|
entry["resolved_sha"] != pin["sha"]
|
|
or entry["oracle_digest"] != pin["oracle_digest"]
|
|
or expected != {entry[DEPENDENCY_CONTENT_BINDING_FIELD]}
|
|
):
|
|
raise ValueError("prepared task/oracle/dependency bytes differ from the historical run")
|
|
|
|
|
|
def _summary(rows: list[dict[str, Any]]) -> dict[str, Any]:
|
|
cost = sum(row["cost_usd"] for row in rows)
|
|
return {
|
|
"cells": len(rows),
|
|
"solved": sum(row["resolved"] for row in rows),
|
|
"total_cost_usd": cost,
|
|
"mean_cost_usd": cost / len(rows),
|
|
"median_cost_usd": statistics.median(row["cost_usd"] for row in rows),
|
|
"mean_wall_s": statistics.mean(row["duration_s"] for row in rows),
|
|
}
|
|
|
|
|
|
def compare_candidate(
|
|
rows: list[Any],
|
|
metadata: dict[str, Any],
|
|
previous: dict[str, Any],
|
|
*,
|
|
candidate_sha: str,
|
|
pr: int,
|
|
baseline_run: int,
|
|
now: datetime | None = None,
|
|
) -> dict[str, Any]:
|
|
from .release_report import ROW_FIELDS, validate_report
|
|
|
|
measured_at = now or datetime.now(UTC)
|
|
validate_report(
|
|
previous, runtime_sha=previous["runtime_sha"], task_set_digest=previous["task_set_digest"], now=measured_at
|
|
)
|
|
problems = []
|
|
if not SHA.fullmatch(candidate_sha) or metadata.get("runtime_sha") != candidate_sha:
|
|
problems.append("candidate runtime does not match the requested PR SHA")
|
|
if not isinstance(metadata.get("harness_sha"), str) or not SHA.fullmatch(metadata["harness_sha"]):
|
|
problems.append("invalid candidate harness SHA")
|
|
for field in ("task_set_digest", "tasks", "model", "effort", "runs"):
|
|
if metadata.get(field) != previous[field]:
|
|
problems.append(f"candidate and historical run use different {field}")
|
|
old_rows = [row for row in previous["per_run"] if row["arm"] == "baseline"]
|
|
old_cells = {(row["task"], row["run"]): row for row in old_rows}
|
|
cells = {}
|
|
safe_rows = []
|
|
for index, row in enumerate(rows):
|
|
if not isinstance(row, dict) or not isinstance(row.get("task"), str) or type(row.get("run")) is not int:
|
|
problems.append(f"row {index}: invalid cell identity")
|
|
continue
|
|
key = row["task"], row["run"]
|
|
old = old_cells.get(key)
|
|
valid = (
|
|
old is not None
|
|
and key not in cells
|
|
and row.get("arm") == "baseline"
|
|
and all(
|
|
row.get(field) == old[field]
|
|
for field in ("model", "effort", "task_base_sha", "oracle_digest", "sandbox_dependency_content_digest")
|
|
)
|
|
and row.get("sandbox_backend") == "bwrap"
|
|
and row.get("ok") is True
|
|
and all(type(row.get(field)) is bool for field in ("resolved", "authored_tests_passed", "oracle_passed"))
|
|
and row["resolved"] == (row["authored_tests_passed"] and row["oracle_passed"])
|
|
and row.get("error_kind") in (None, "verify-failed", "oracle-failed")
|
|
and row.get("transcript_missing") is False
|
|
and _number(row.get("cost_usd"))
|
|
and _number(row.get("duration_s"))
|
|
)
|
|
safe = {field: row[field] for field in ROW_FIELDS if field in row}
|
|
try:
|
|
json.dumps(safe, allow_nan=False)
|
|
except (ValueError, TypeError, OverflowError):
|
|
valid = False
|
|
if not valid:
|
|
problems.append(f"row {index}: duplicate, unexpected or untrustworthy measurement")
|
|
continue
|
|
cells[key] = row
|
|
safe_rows.append(safe)
|
|
if set(cells) != set(old_cells):
|
|
problems.append("missing historical task/repetition pairs")
|
|
result: dict[str, Any] = {
|
|
"schema": "gitnexus.pr-cost-comparison/v1",
|
|
"generated_at": measured_at.isoformat(),
|
|
"purpose": "PR measurement, not release approval",
|
|
"pr": pr,
|
|
"candidate_sha": candidate_sha,
|
|
"baseline_run": baseline_run,
|
|
"baseline_runtime_sha": previous["runtime_sha"],
|
|
"baseline_harness_sha": previous["harness_sha"],
|
|
"harness_sha": metadata.get("harness_sha"),
|
|
"model": previous["model"],
|
|
"effort": previous["effort"],
|
|
"runs": previous["runs"],
|
|
"task_set_digest": previous["task_set_digest"],
|
|
"complete": not problems,
|
|
"problems": problems,
|
|
"quality_preserved": False,
|
|
"improved": False,
|
|
"cost_reduction_pct": None,
|
|
"previous": _summary(old_rows),
|
|
"candidate": None,
|
|
"tasks": {},
|
|
"per_run": safe_rows,
|
|
}
|
|
if problems:
|
|
return result
|
|
result["candidate"] = _summary(list(cells.values()))
|
|
quality = True
|
|
for task in previous["tasks"]:
|
|
old = _summary([row for row in old_rows if row["task"] == task])
|
|
new = _summary([row for row in cells.values() if row["task"] == task])
|
|
delta = new["solved"] - old["solved"]
|
|
quality = quality and delta >= 0
|
|
result["tasks"][task] = {"previous": old, "candidate": new, "solve_delta": delta}
|
|
old_cost, new_cost = result["previous"]["total_cost_usd"], result["candidate"]["total_cost_usd"]
|
|
result.update(
|
|
quality_preserved=quality,
|
|
improved=quality and new_cost < old_cost,
|
|
cost_reduction_pct=100 * (old_cost - new_cost) / old_cost if old_cost else None,
|
|
)
|
|
return result
|
|
|
|
|
|
def render_markdown(report: dict[str, Any]) -> str:
|
|
lines = [
|
|
"## PR cost comparison",
|
|
"",
|
|
"PR measurement, not release approval.",
|
|
"",
|
|
f"PR #{report['pr']} at `{report['candidate_sha']}` vs run {report['baseline_run']}.",
|
|
f"Model `{report['model']}`, effort `{report['effort']}`, n={report['runs']} per task.",
|
|
"",
|
|
]
|
|
if not report["complete"]:
|
|
return "\n".join([*lines, "**Incomplete evidence; no savings conclusion.**", "", *report["problems"], ""])
|
|
lines += [
|
|
"| MCP sessions | Solved | Total model cost | Mean model cost |",
|
|
"| --- | --- | --- | --- |",
|
|
]
|
|
for label, key in (("Previous run", "previous"), ("PR candidate", "candidate")):
|
|
row = report[key]
|
|
lines.append(
|
|
f"| {label} | {row['solved']}/{row['cells']} | ${row['total_cost_usd']:.4f} | ${row['mean_cost_usd']:.4f} |"
|
|
)
|
|
reduction = report["cost_reduction_pct"]
|
|
lines += [
|
|
"",
|
|
f"Cost reduction: {reduction:+.1f}%."
|
|
if reduction is not None
|
|
else "Cost reduction unavailable: zero historical cost.",
|
|
f"Per-task quality preserved: {'yes' if report['quality_preserved'] else 'NO'}.",
|
|
"",
|
|
"| Task | Previous solved | PR solved | Previous cost | PR cost |",
|
|
"| --- | --- | --- | --- | --- |",
|
|
]
|
|
for task, row in report["tasks"].items():
|
|
old, new = row["previous"], row["candidate"]
|
|
lines.append(
|
|
f"| {task} | {old['solved']}/{old['cells']} | {new['solved']}/{new['cells']} | ${old['total_cost_usd']:.4f} | ${new['total_cost_usd']:.4f} |"
|
|
)
|
|
return "\n".join(
|
|
[
|
|
*lines,
|
|
"",
|
|
"Only the PR MCP cells are newly measured; no-MCP and stable sessions are not rerun.",
|
|
"Historical comparisons are descriptive and do not establish statistical significance or isolate this PR from other runtime changes.",
|
|
"",
|
|
]
|
|
)
|
|
|
|
|
|
def main() -> None:
|
|
parser = argparse.ArgumentParser(description=__doc__)
|
|
commands = parser.add_subparsers(dest="command", required=True)
|
|
dispatch = commands.add_parser("dispatch-label")
|
|
dispatch.add_argument("--repo", required=True)
|
|
dispatch.add_argument("--event", type=Path, required=True)
|
|
resolve = commands.add_parser("resolve")
|
|
resolve.add_argument("--repo", required=True)
|
|
resolve.add_argument("--pr", type=int, required=True)
|
|
resolve.add_argument("--sha", required=True)
|
|
baseline = commands.add_parser("baseline")
|
|
for flag in ("repo", "harness-sha", "model", "effort"):
|
|
baseline.add_argument(f"--{flag}", required=True)
|
|
baseline.add_argument("--run-id", type=int, required=True)
|
|
baseline.add_argument("--out", type=Path, required=True)
|
|
dependencies = commands.add_parser("dependencies")
|
|
dependencies.add_argument("--tasks", type=Path, required=True)
|
|
dependencies.add_argument("--baseline", type=Path, required=True)
|
|
summarize = commands.add_parser("summarize")
|
|
for flag in ("results", "metadata", "baseline", "out"):
|
|
summarize.add_argument(f"--{flag}", type=Path, required=True)
|
|
summarize.add_argument("--sha", required=True)
|
|
summarize.add_argument("--pr", type=int, required=True)
|
|
summarize.add_argument("--run-id", type=int, required=True)
|
|
args = parser.parse_args()
|
|
if args.command == "dispatch-label":
|
|
event = json.loads(args.event.read_text())
|
|
run_id = dispatch_labeled_pr(args.repo, event)
|
|
print(f"pr={event['number']}")
|
|
print(f"sha={event['pull_request']['head']['sha']}")
|
|
print(f"comparison_run_id={run_id}")
|
|
elif args.command == "resolve":
|
|
print(f"sha={resolve_pr(args.repo, args.pr, args.sha)}")
|
|
elif args.command == "baseline":
|
|
report = fetch_baseline(args.repo, args.run_id, args.harness_sha, args.model, args.effort)
|
|
args.out.parent.mkdir(parents=True, exist_ok=True)
|
|
args.out.write_text(json.dumps(report, indent=2, allow_nan=False) + "\n")
|
|
elif args.command == "dependencies":
|
|
import yaml
|
|
|
|
from .runner_tasks import resolve_task_bindings, select_tasks
|
|
from .task_assets import TaskAssetCache
|
|
|
|
tasks, _ = select_tasks(yaml.safe_load(args.tasks.read_text())["tasks"], include_expensive=True)
|
|
with (
|
|
tempfile.TemporaryDirectory(prefix="pr-evaluation-dependencies-") as directory,
|
|
TaskAssetCache(Path(directory) / "cache") as cache,
|
|
):
|
|
bindings = resolve_task_bindings(tasks, task_asset_cache=cache)
|
|
check_dependency_bindings(bindings, json.loads(args.baseline.read_text()))
|
|
else:
|
|
rows = []
|
|
for line in args.results.read_text().splitlines() if args.results.is_file() else []:
|
|
if line.strip():
|
|
try:
|
|
rows.append(json.loads(line))
|
|
except json.JSONDecodeError:
|
|
rows.append(None)
|
|
report = compare_candidate(
|
|
rows,
|
|
json.loads(args.metadata.read_text()),
|
|
json.loads(args.baseline.read_text()),
|
|
candidate_sha=args.sha,
|
|
pr=args.pr,
|
|
baseline_run=args.run_id,
|
|
)
|
|
args.out.mkdir(parents=True, exist_ok=True)
|
|
(args.out / "pr-cost-comparison.json").write_text(json.dumps(report, indent=2, allow_nan=False) + "\n")
|
|
(args.out / "pr-cost-comparison.md").write_text(render_markdown(report))
|
|
if not report["complete"] or not report["quality_preserved"]:
|
|
raise SystemExit(1)
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|