Files
ai-agent-book/chapter8/continued-pretraining/validation/run_report_audit.py
T
liqiang b119135836
Build latest book artifacts / build (push) Canceled after 0s
dependency resolution / resolve (3.11) (push) Canceled after 0s
dependency resolution / resolve (3.13) (push) Canceled after 0s
deploy-pages / build (push) Canceled after 0s
deploy-pages / deploy (push) Canceled after 0s
i18n consistency check / check (push) Canceled after 0s
provider adoption tests / test (chapter2/context-compression) (push) Canceled after 0s
provider adoption tests / test (chapter2/prompt-injection) (push) Canceled after 0s
provider adoption tests / test (chapter2/system-hint) (push) Canceled after 0s
provider adoption tests / test (chapter3/log-sanitization) (push) Canceled after 0s
web-search-agent tests / test (push) Canceled after 0s
web-search-agent tests / agentbook (push) Canceled after 0s
ai-agent-book 精选快照(<2MB 代码与文档,来自 github.com/bojieli/ai-agent-book)
2026-08-20 13:12:50 +00:00

781 lines
30 KiB
Python

#!/usr/bin/env python3
"""Build the canonical, checkpoint-free evidence package for Experiment 8-5.
The historical RTX 4090 run is retained as a raw terminal transcript in
``model_eval_results.md``. This tool does not pretend to rerun that GPU job.
It extracts the fifteen saved generations, submits five stage-blind comparison
tasks to an independent judge, and binds the report, current reproduction
sources, frozen upstream revisions, receipts, findings, and limitations into a
content-hashed manifest.
"""
from __future__ import annotations
import argparse
import concurrent.futures
import hashlib
import json
import os
import random
import re
import time
import urllib.error
import urllib.request
from datetime import datetime, timezone
from pathlib import Path
from typing import Any
HERE = Path(__file__).resolve().parent
EXPERIMENT_DIR = HERE.parent
REPO_ROOT = EXPERIMENT_DIR.parents[1]
REPORT_PATH = EXPERIMENT_DIR / "model_eval_results.md"
RUNS_DIR = HERE / "runs"
LATEST_PATH = HERE / "latest.json"
DEFAULT_RUN_ID = "exp8-5-training-report-20260731-v1"
DEFAULT_ENDPOINT = "https://ark.cn-beijing.volces.com/api/v3/chat/completions"
DEFAULT_MODEL = "doubao-seed-1-6-250615"
BLIND_SEED = 750731
ENGLISH_RETENTION_TOLERANCE = 1.0
BASE_REVISION = "9ea1b83f5ced5663c5fa89c300fe59f9bdcd2b10"
WIKIPEDIA_REVISION = "b04c8d1ceb2f5cd4588862100d08de323dccfbaa"
ALPACA_REVISION = "f38ae19cf673363d74fab6217de46c1b9c3150d4"
TEST_RE = re.compile(r"^Test ([1-5]):\s*(.+)$")
BOX_RE = re.compile(r"^┌─ (BASELINE|PRETRAINED|FINETUNED) MODEL\b")
CLOSE_RE = re.compile(r"^└─+")
STAGES = ("baseline", "pretrained", "finetuned")
LABELS = ("A", "B", "C")
PROMPTS = {
1: {
"language": "korean",
"task": "Write the opening of a Korean Wikipedia article about artificial intelligence.",
},
2: {
"language": "english",
"task": "Write the opening of an English Wikipedia article about artificial intelligence.",
},
3: {
"language": "korean",
"task": "한국의 전통 음식인 김치에 대해 설명하세요.",
},
4: {
"language": "korean",
"task": "대한민국의 수도인 서울에 대해 간단히 소개해주세요.",
},
5: {
"language": "english",
"task": "Explain Thanksgiving turkey, a traditional American food.",
},
}
def sha256_bytes(data: bytes) -> str:
return hashlib.sha256(data).hexdigest()
def sha256_file(path: Path) -> str:
digest = hashlib.sha256()
with path.open("rb") as handle:
for chunk in iter(lambda: handle.read(1024 * 1024), b""):
digest.update(chunk)
return digest.hexdigest()
def canonical_json_bytes(value: Any) -> bytes:
return (json.dumps(value, ensure_ascii=False, indent=2, sort_keys=True) + "\n").encode()
def write_json(path: Path, value: Any) -> None:
path.write_bytes(canonical_json_bytes(value))
def utc_now() -> str:
return datetime.now(timezone.utc).isoformat()
def parse_retained_outputs(report_path: Path = REPORT_PATH) -> dict[str, Any]:
"""Extract the five-by-three raw comparison matrix from the saved report."""
lines = report_path.read_text(encoding="utf-8").splitlines()
tests: dict[int, dict[str, Any]] = {}
current_test: int | None = None
current_stage: str | None = None
body: list[str] = []
for line in lines:
test_match = TEST_RE.match(line)
if test_match:
current_test = int(test_match.group(1))
if current_test in tests:
raise ValueError(f"duplicate raw Test {current_test}")
tests[current_test] = {
"test_id": current_test,
"name": test_match.group(2).strip(),
"language": PROMPTS[current_test]["language"],
"task": PROMPTS[current_test]["task"],
"outputs": {},
}
continue
box_match = BOX_RE.match(line)
if box_match:
if current_test is None:
raise ValueError("model output box appeared before a raw Test heading")
if current_stage is not None:
raise ValueError("nested model output boxes")
current_stage = box_match.group(1).lower()
body = []
continue
if current_stage is None:
continue
if CLOSE_RE.match(line):
output = "\n".join(body).strip()
if not output:
raise ValueError(f"empty {current_stage} output in Test {current_test}")
outputs = tests[current_test]["outputs"]
if current_stage in outputs:
raise ValueError(f"duplicate {current_stage} output in Test {current_test}")
outputs[current_stage] = output
current_stage = None
body = []
continue
if line == "│":
body.append("")
elif line.startswith("│ "):
body.append(line[2:])
elif line.startswith("│"):
body.append(line[1:].lstrip())
else:
# The historical terminal capture wrapped a few long lines without
# repeating the box prefix. Preserve those bytes as output text.
body.append(line)
if current_stage is not None:
raise ValueError("unterminated model output box")
if set(tests) != set(PROMPTS):
raise ValueError(f"expected Tests 1-5, found {sorted(tests)}")
for test_id, test in tests.items():
if set(test["outputs"]) != set(STAGES):
raise ValueError(
f"Test {test_id} expected stages {STAGES}, found {sorted(test['outputs'])}"
)
ordered = [tests[test_id] for test_id in sorted(tests)]
return {
"schema_version": "exp8-5-retained-outputs-v1",
"source_report": str(REPORT_PATH.relative_to(REPO_ROOT)),
"source_report_sha256": sha256_file(report_path),
"test_count": len(ordered),
"output_count": sum(len(test["outputs"]) for test in ordered),
"tests": ordered,
}
def blind_mapping(test_id: int) -> dict[str, str]:
stages = list(STAGES)
random.Random(BLIND_SEED + test_id).shuffle(stages)
return dict(zip(LABELS, stages, strict=True))
def judge_payload(test: dict[str, Any], mapping: dict[str, str], model: str) -> dict[str, Any]:
candidates = {
label: test["outputs"][stage]
for label, stage in mapping.items()
}
rubric = {
"language_fluency": "0 unreadable; 3 understandable with defects; 5 native-quality and coherent",
"instruction_following": "0 ignores the task; 3 partly satisfies it; 5 directly and fully satisfies it",
"factuality": "0 dominated by falsehoods; 3 mixed/minor errors; 5 accurate with no material error",
}
expected_shape = {
"test_id": test["test_id"],
"language": test["language"],
"candidates": {
label: {
"language_fluency": "number 0-5",
"instruction_following": "number 0-5",
"factuality": "number 0-5",
"factual_errors": ["specific error, empty only if none"],
"rationale": "short evidence-based explanation",
}
for label in LABELS
},
"ranking": ["best label", "middle label", "worst label"],
}
user_content = {
"test_id": test["test_id"],
"language": test["language"],
"task": test["task"],
"rubric": rubric,
"candidates": candidates,
"required_json_shape": expected_shape,
}
return {
"model": model,
"temperature": 0,
"response_format": {"type": "json_object"},
"messages": [
{
"role": "system",
"content": (
"You are an independent bilingual Korean/English evaluator. "
"The candidates are deliberately anonymous; do not infer model identity or training stage. "
"Score only the supplied text. Identify concrete factual errors, especially invented food "
"ingredients or preparation claims. Return one JSON object only, with every requested field."
),
},
{
"role": "user",
"content": json.dumps(user_content, ensure_ascii=False, sort_keys=True),
},
],
}
def extract_json_object(content: str) -> dict[str, Any]:
stripped = content.strip()
if stripped.startswith("```"):
stripped = re.sub(r"^```(?:json)?\s*", "", stripped)
stripped = re.sub(r"\s*```$", "", stripped)
parsed = json.loads(stripped)
if not isinstance(parsed, dict):
raise ValueError("judge content must decode to an object")
return parsed
def validate_judgment(judgment: dict[str, Any], test: dict[str, Any]) -> None:
if judgment.get("test_id") != test["test_id"]:
raise ValueError("judge returned the wrong test_id")
if judgment.get("language") != test["language"]:
raise ValueError("judge returned the wrong language")
candidates = judgment.get("candidates")
if not isinstance(candidates, dict) or set(candidates) != set(LABELS):
raise ValueError("judge must score exactly candidates A, B, and C")
for label in LABELS:
row = candidates[label]
if not isinstance(row, dict):
raise ValueError(f"candidate {label} score must be an object")
for metric in ("language_fluency", "instruction_following", "factuality"):
score = row.get(metric)
if not isinstance(score, (int, float)) or isinstance(score, bool) or not 0 <= score <= 5:
raise ValueError(f"candidate {label} has invalid {metric}: {score!r}")
errors = row.get("factual_errors")
if not isinstance(errors, list) or not all(isinstance(item, str) for item in errors):
raise ValueError(f"candidate {label} factual_errors must be a list of strings")
if not isinstance(row.get("rationale"), str) or not row["rationale"].strip():
raise ValueError(f"candidate {label} rationale is missing")
ranking = judgment.get("ranking")
if not isinstance(ranking, list) or set(ranking) != set(LABELS) or len(ranking) != 3:
raise ValueError("judge ranking must contain A, B, and C exactly once")
def call_judge(
test: dict[str, Any],
*,
endpoint: str,
model: str,
api_key: str,
timeout: float,
) -> dict[str, Any]:
mapping = blind_mapping(test["test_id"])
payload = judge_payload(test, mapping, model)
request = urllib.request.Request(
endpoint,
data=json.dumps(payload, ensure_ascii=False).encode("utf-8"),
method="POST",
headers={
"Authorization": f"Bearer {api_key}",
"Content-Type": "application/json",
},
)
started = time.perf_counter()
try:
with urllib.request.urlopen(request, timeout=timeout) as response:
response_body = response.read()
http_status = response.status
except urllib.error.HTTPError as exc:
body = exc.read().decode("utf-8", errors="replace")
raise RuntimeError(f"judge HTTP {exc.code}: {body[:500]}") from exc
latency_ms = round((time.perf_counter() - started) * 1000, 3)
raw_response = json.loads(response_body)
try:
content = raw_response["choices"][0]["message"]["content"]
except (KeyError, IndexError, TypeError) as exc:
raise ValueError("judge response has no choices[0].message.content") from exc
judgment = extract_json_object(content)
validate_judgment(judgment, test)
response_id = raw_response.get("id")
usage = raw_response.get("usage")
if not isinstance(response_id, str) or not response_id:
raise ValueError("judge response has no response ID")
if not isinstance(usage, dict) or not isinstance(usage.get("total_tokens"), int):
raise ValueError("judge response has no complete usage object")
return {
"test_id": test["test_id"],
"provider": "ark",
"endpoint": endpoint,
"credential_env": "ARK_API_KEY",
"blind_seed": BLIND_SEED,
"blind_map": mapping,
"request": payload,
"http_status": http_status,
"response": raw_response,
"response_id": response_id,
"usage": usage,
"latency_ms": latency_ms,
"judgment": judgment,
}
def reproduction_contract() -> dict[str, Any]:
pin_note = (
"This immutable revision is the frozen reproduction contract selected on 2026-07-31. "
"The historical run did not retain its resolved upstream commit, so this is not claimed "
"to be the exact historical revision."
)
return {
"schema_version": "exp8-5-reproduction-contract-v1",
"experiment": "8-5",
"historical_evidence_boundary": {
"historical_training_executed": True,
"raw_three_stage_evaluation_retained": True,
"historical_upstream_revisions_retained": False,
"historical_checkpoint_hashes_retained": False,
"claim": (
"The retained terminal report proves a three-stage evaluation ran on the reported RTX 4090 "
"software stack. It does not prove the byte identity of the historical adapters or upstream data."
),
},
"upstream_revisions": {
"base_model": {
"repository": "unsloth/mistral-7b-v0.3",
"revision": BASE_REVISION,
"note": pin_note,
},
"continued_pretraining_dataset": {
"repository": "wikimedia/wikipedia",
"configuration": "20231101.ko",
"revision": WIKIPEDIA_REVISION,
"note": pin_note,
},
"instruction_dataset": {
"repository": "FreedomIntelligence/alpaca-gpt4-korean",
"revision": ALPACA_REVISION,
"note": pin_note,
},
},
"training": {
"model_loading": {"max_sequence_length": 2048, "load_in_4bit": True},
"lora": {
"rank": 128,
"alpha": 32,
"dropout": 0,
"bias": "none",
"use_rslora": True,
"random_state": 3407,
"gradient_checkpointing": "unsloth",
"target_modules": [
"q_proj",
"k_proj",
"v_proj",
"o_proj",
"gate_proj",
"up_proj",
"down_proj",
"embed_tokens",
"lm_head",
],
},
"continued_pretraining": {
"dataset_fraction": 0.05,
"epochs": 1,
"max_steps": -1,
"batch_size": 2,
"gradient_accumulation_steps": 8,
"learning_rate": 5e-5,
"embedding_learning_rate": 1e-5,
"warmup_steps": 10,
"warmup_ratio": 0.1,
"optimizer": "adamw_8bit",
"weight_decay": 0.01,
"scheduler": "linear",
"trainer_seed": 42,
"dataset_split_seed": "not explicitly recorded by the historical script",
},
"instruction_sft": {
"epochs": 2,
"max_steps": -1,
"batch_size": 2,
"gradient_accumulation_steps": 8,
"learning_rate": 5e-5,
"embedding_learning_rate": 1e-5,
"warmup_steps": 10,
"warmup_ratio": 0.1,
"optimizer": "adamw_8bit",
"weight_decay": 0.0,
"scheduler": "linear",
"trainer_seed": 42,
},
},
"evaluation": {
"stages": list(STAGES),
"test_count": 5,
"output_count": 15,
"max_new_tokens": 150,
"temperature": 0.3,
"do_sample": True,
"historical_generation_seed": "not retained",
},
"historical_environment_from_report": {
"gpu": "NVIDIA GeForce RTX 4090",
"gpu_memory_gb": 23.647,
"platform": "Linux",
"torch": "2.8.0+cu128",
"cuda_compute_capability": "8.9",
"cuda_toolkit": "12.8",
"unsloth": "2025.10.4",
"transformers": "4.56.2",
"triton": "3.4.0",
"xformers": "0.0.32.post2",
},
"checkpoint_policy": {
"distributed_with_book": False,
"acceptance_artifact": False,
"required_artifact": "reproducible evidence-backed training report",
"reason": "Training adapters are intentionally local and are not distributed to readers.",
},
}
def mean(values: list[float]) -> float:
return round(sum(values) / len(values), 4)
def summarize(
retained: dict[str, Any], receipts: list[dict[str, Any]], contract: dict[str, Any]
) -> dict[str, Any]:
by_test = {test["test_id"]: test for test in retained["tests"]}
stage_rows: dict[int, dict[str, dict[str, Any]]] = {}
for receipt in receipts:
reverse = {label: stage for label, stage in receipt["blind_map"].items()}
stage_rows[receipt["test_id"]] = {
reverse[label]: score
for label, score in receipt["judgment"]["candidates"].items()
}
metrics = ("language_fluency", "instruction_following", "factuality")
stage_averages: dict[str, dict[str, Any]] = {}
for stage in STAGES:
korean_rows = [
stage_rows[test_id][stage]
for test_id in (1, 3, 4)
]
english_rows = [
stage_rows[test_id][stage]
for test_id in (2, 5)
]
stage_averages[stage] = {
"korean": {
metric: mean([float(row[metric]) for row in korean_rows])
for metric in metrics
},
"english": {
metric: mean([float(row[metric]) for row in english_rows])
for metric in metrics
},
}
stage_averages[stage]["korean"]["overall"] = mean(
[float(row[metric]) for row in korean_rows for metric in metrics]
)
stage_averages[stage]["english"]["overall"] = mean(
[float(row[metric]) for row in english_rows for metric in metrics]
)
baseline_korean = stage_averages["baseline"]["korean"]["overall"]
final_korean = stage_averages["finetuned"]["korean"]["overall"]
baseline_english = stage_averages["baseline"]["english"]["overall"]
final_english = stage_averages["finetuned"]["english"]["overall"]
english_drop = round(baseline_english - final_english, 4)
kimchi_errors = stage_rows[3]["finetuned"]["factual_errors"]
findings = {
"korean_gain_observed": final_korean > baseline_korean,
"korean_gain": round(final_korean - baseline_korean, 4),
"english_retention_tolerance": ENGLISH_RETENTION_TOLERANCE,
"english_drop": english_drop,
"english_retention_within_tolerance": english_drop <= ENGLISH_RETENTION_TOLERANCE,
"kimchi_factual_failure_observed": bool(kimchi_errors),
"kimchi_finetuned_factual_errors": kimchi_errors,
}
execution_gates = {
"raw_report_hashed": bool(retained["source_report_sha256"]),
"exactly_five_tests": retained["test_count"] == 5,
"exactly_fifteen_outputs": retained["output_count"] == 15,
"all_three_stages_retained": all(
set(test["outputs"]) == set(STAGES) for test in retained["tests"]
),
"five_independent_blind_judgments": len(receipts) == 5,
"judge_response_ids_usage_and_latency_retained": all(
receipt["response_id"]
and receipt["usage"].get("total_tokens", 0) > 0
and receipt["latency_ms"] > 0
for receipt in receipts
),
"training_and_evaluation_sources_declared": True,
"immutable_future_reproduction_revisions_frozen": all(
contract["upstream_revisions"][key]["revision"]
for key in (
"base_model",
"continued_pretraining_dataset",
"instruction_dataset",
)
),
"historical_revision_boundary_explicit": (
contract["historical_evidence_boundary"]["historical_upstream_revisions_retained"]
is False
),
"checkpoints_not_an_acceptance_artifact": (
contract["checkpoint_policy"]["acceptance_artifact"] is False
),
# Scientific outcomes are reported, not promoted into evidence-completeness
# gates. A real negative result still completes the prescribed comparison.
"korean_gain_comparison_completed": isinstance(findings["korean_gain"], float),
"english_retention_comparison_completed": isinstance(findings["english_drop"], float),
"kimchi_failure_explicitly_reported": findings["kimchi_factual_failure_observed"],
}
passed = all(execution_gates.values())
return {
"schema_version": "exp8-5-summary-v1",
"experiment": "8-5",
"status": "passed" if passed else "failed",
"judge": {
"provider": "ark",
"model": receipts[0]["request"]["model"],
"calls": len(receipts),
"response_ids": [receipt["response_id"] for receipt in receipts],
"total_tokens": sum(receipt["usage"]["total_tokens"] for receipt in receipts),
"total_latency_ms": round(sum(receipt["latency_ms"] for receipt in receipts), 3),
"blind_seed": BLIND_SEED,
},
"stage_averages": stage_averages,
"per_test_stage_scores": stage_rows,
"scientific_findings": findings,
"acceptance": {**execution_gates, "passed": passed},
"limitations": [
"The historical adapters/checkpoints are intentionally not distributed and were not re-created.",
"The exact historical upstream revisions and generation RNG seed were not retained.",
"The frozen upstream revisions are a future reproduction contract, not historical provenance.",
"The retained evaluation has five prompts and one sampled generation per stage/prompt.",
],
"test_names": {str(test_id): by_test[test_id]["name"] for test_id in sorted(by_test)},
}
def render_report(summary: dict[str, Any]) -> str:
averages = summary["stage_averages"]
findings = summary["scientific_findings"]
rows = []
for stage in STAGES:
rows.append(
f"| {stage} | {averages[stage]['korean']['overall']:.4f} | "
f"{averages[stage]['english']['overall']:.4f} |"
)
kimchi = "; ".join(findings["kimchi_finetuned_factual_errors"])
return "\n".join(
[
"# Experiment 8-5 retained-training-report audit",
"",
"## Result",
"",
f"Status: **{summary['status']}**. The historical RTX 4090 report contains all five "
"prompts across the baseline, continued-pretrained, and instruction-tuned stages. "
"An independent stage-blind ARK judge scored the exact 15 retained outputs.",
"",
"| Stage | Korean mean (0-5) | English mean (0-5) |",
"| --- | ---: | ---: |",
*rows,
"",
f"Observed Korean gain, final minus baseline: **{findings['korean_gain']:+.4f}**.",
f"Observed English drop, baseline minus final: **{findings['english_drop']:+.4f}** "
f"(declared tolerance: {findings['english_retention_tolerance']:.1f}).",
(
"The final English score is within the declared tolerance."
if findings["english_retention_within_tolerance"]
else "The final English score is outside the declared tolerance; the historical retention "
"claim is not supported by this blind audit."
),
"",
"## Material negative result",
"",
"The final model's Korean is more fluent, but the kimchi answer remains factually unsafe. "
f"The blind judge identified: {kimchi}",
"",
"## Provenance boundary",
"",
"The raw terminal report records the historical GPU/software identity and generated text, "
"but not adapter hashes, the exact resolved upstream commits, or the sampling seed. The "
"immutable Hugging Face revisions in `reproduction_contract.json` were selected on "
"2026-07-31 for future reproduction and are not represented as the historical revisions.",
"",
"Checkpoints are intentionally local and are not an acceptance artifact. The accepted "
"book artifact is this reproducible, evidence-backed training report.",
"",
]
)
def input_record(path: Path) -> dict[str, Any]:
return {
"path": str(path.relative_to(REPO_ROOT)),
"sha256": sha256_file(path),
"bytes": path.stat().st_size,
}
def artifact_record(path: Path, run_dir: Path) -> dict[str, Any]:
return {
"path": str(path.relative_to(run_dir)),
"sha256": sha256_file(path),
"bytes": path.stat().st_size,
}
def build_manifest(run_id: str, run_dir: Path, summary: dict[str, Any]) -> dict[str, Any]:
inputs = [
input_record(REPORT_PATH),
input_record(EXPERIMENT_DIR / "continued-pretrain.py"),
input_record(EXPERIMENT_DIR / "compare_models.py"),
input_record(EXPERIMENT_DIR / "evaluate_model.py"),
input_record(HERE / "run_report_audit.py"),
input_record(HERE / "validate_evidence.py"),
]
artifact_paths = [
run_dir / "retained_outputs.json",
run_dir / "reproduction_contract.json",
run_dir / "judge_receipts.json",
run_dir / "summary.json",
run_dir / "report.md",
]
return {
"schema_version": "exp8-5-manifest-v1",
"experiment": "8-5",
"run_id": run_id,
"created_at": utc_now(),
"status": summary["status"],
"run_dir": str(run_dir.relative_to(EXPERIMENT_DIR)),
"inputs": inputs,
"artifacts": [artifact_record(path, run_dir) for path in artifact_paths],
"acceptance": summary["acceptance"],
"checkpoint_policy": "not distributed; not an acceptance artifact",
}
def parse_args() -> argparse.Namespace:
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument("--run-id", default=DEFAULT_RUN_ID)
parser.add_argument("--endpoint", default=os.getenv("ARK_BASE_URL", DEFAULT_ENDPOINT))
parser.add_argument("--model", default=os.getenv("ARK_MODEL", DEFAULT_MODEL))
parser.add_argument("--api-key-env", default="ARK_API_KEY")
parser.add_argument("--timeout", type=float, default=180.0)
parser.add_argument("--concurrency", type=int, default=5)
parser.add_argument(
"--refresh-manifest",
action="store_true",
help="Rehash an existing run after pre-commit source-only corrections; makes no provider call.",
)
return parser.parse_args()
def main() -> int:
args = parse_args()
if not re.fullmatch(r"[A-Za-z0-9._-]+", args.run_id):
raise SystemExit("run ID may contain only letters, digits, dot, underscore, and hyphen")
run_dir = RUNS_DIR / args.run_id
if args.refresh_manifest:
if not run_dir.is_dir():
raise SystemExit(f"cannot refresh missing run: {run_dir}")
summary = json.loads((run_dir / "summary.json").read_text(encoding="utf-8"))
manifest = build_manifest(args.run_id, run_dir, summary)
write_json(run_dir / "manifest.json", manifest)
latest = {
"schema_version": "exp8-5-latest-v1",
"experiment": "8-5",
"run_id": args.run_id,
"status": summary["status"],
"run_dir": str(run_dir.relative_to(EXPERIMENT_DIR)),
"manifest_sha256": sha256_file(run_dir / "manifest.json"),
}
write_json(LATEST_PATH, latest)
print(json.dumps(latest, indent=2, sort_keys=True))
return 0
if run_dir.exists():
raise SystemExit(f"refusing to overwrite existing run: {run_dir}")
api_key = os.getenv(args.api_key_env)
if not api_key:
raise SystemExit(f"{args.api_key_env} is required for the independent judge")
retained = parse_retained_outputs()
if not 1 <= args.concurrency <= 5:
raise SystemExit("concurrency must be between 1 and 5")
with concurrent.futures.ThreadPoolExecutor(max_workers=args.concurrency) as executor:
receipts = list(
executor.map(
lambda test: call_judge(
test,
endpoint=args.endpoint,
model=args.model,
api_key=api_key,
timeout=args.timeout,
),
retained["tests"],
)
)
contract = reproduction_contract()
summary = summarize(retained, receipts, contract)
if summary["status"] != "passed":
failed = [key for key, value in summary["acceptance"].items() if value is False]
raise SystemExit(f"acceptance failed; no canonical run written: {failed}")
run_dir.mkdir(parents=True)
write_json(run_dir / "retained_outputs.json", retained)
write_json(run_dir / "reproduction_contract.json", contract)
write_json(
run_dir / "judge_receipts.json",
{
"schema_version": "exp8-5-judge-receipts-v1",
"experiment": "8-5",
"credential_headers_retained": False,
"calls": receipts,
},
)
write_json(run_dir / "summary.json", summary)
(run_dir / "report.md").write_text(render_report(summary), encoding="utf-8")
manifest = build_manifest(args.run_id, run_dir, summary)
write_json(run_dir / "manifest.json", manifest)
latest = {
"schema_version": "exp8-5-latest-v1",
"experiment": "8-5",
"run_id": args.run_id,
"status": summary["status"],
"run_dir": str(run_dir.relative_to(EXPERIMENT_DIR)),
"manifest_sha256": sha256_file(run_dir / "manifest.json"),
}
write_json(LATEST_PATH, latest)
print(json.dumps(latest, indent=2, sort_keys=True))
return 0
if __name__ == "__main__":
raise SystemExit(main())