Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 21 additions & 6 deletions docs/gpt61sol/design.md

Large diffs are not rendered by default.

6 changes: 6 additions & 0 deletions docs/gpt61sol/release_receipt.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"note": "The GPT-6.1 Sol stage's export receipt, written by scripts/finish_gpt61sol.py --step export: the release tag and the sha256 of the staged payload and of release-ready.json. The freeze reads this file as committed at HEAD and refuses a stage whose receipt or payload it does not name.",
"payload_sha256": "d1cae7456cf91ab6fa04644a4d5d570e359522386a7c52f9645d5923bfc11258",
"release_ready_sha256": "e8e0a51f28782d08fd9fb426fbf5cbe7a510fab75abfb949f799f92cf87773d7",
"release_tag": "dashboard-data-20260930"
}
1,350 changes: 675 additions & 675 deletions docs/gpt61sol/seed_digest.csv

Large diffs are not rendered by default.

558 changes: 504 additions & 54 deletions scripts/finish_gpt61sol.py

Large diffs are not rendered by default.

41 changes: 35 additions & 6 deletions scripts/freeze_gpt61sol.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,8 @@

Adapted from freeze_adds0928.py. It configures the existing freezer in process
and never calls a release upload. Run only after finish_gpt61sol.py --step
export has written release-ready.json. The optional --dry-run validates inputs
export has written release-ready.json and its pin, committed at HEAD
(docs/gpt61sol/release_receipt.json). The optional --dry-run validates inputs
without changing repository files. Both rebuild the payload from the bound
bundle, as export builds it, and refuse a staged payload that differs. This
release has no reference revision: the references, exclusions and scenarios
Expand Down Expand Up @@ -41,6 +42,9 @@
ADJUDICATIONS = "us_adjudications.json"
# The published annotations the adjudications and amendments are applied to.
ANNOTATION_CSVS = ("us_audit_row_annotations.csv", "us_case_notes.csv")
# Every annotation file the freeze publishes, the case reference explanations
# included; the receipt must bind each.
ANNOTATION_FILES = driver.ANNOTATION_FILES


def read_json(path: Path) -> dict:
Expand Down Expand Up @@ -125,8 +129,8 @@ def verify_receipt(stage: Path, payload_path: Path, tag: str) -> dict:
raise SystemExit(f"Staged evidence changed since strict export: {name}")
bundle = Path("publish") / RUN
required = [bundle / "us" / name for name in driver.REFERENCE_FILES]
required += [bundle / "us/predictions.csv", bundle / "annotations" / ADJUDICATIONS]
required += [bundle / "annotations" / name for name in ANNOTATION_CSVS]
required += [bundle / "us/predictions.csv"]
required += [bundle / "annotations" / name for name in ANNOTATION_FILES]
# Each new model's pinned run files, and the prepare-time hashes.
required += [
Path("inputs") / slug / name
Expand Down Expand Up @@ -298,7 +302,10 @@ def rebuild_payload(stage: Path, receipt: dict, payload_path: Path, live: dict)
against its receipt hash, and its bytes must equal the staged payload's.
The copy is the bundle's: export_full_run writes data.json, us/data.json
and us/analysis/ into the bundle it reads, and its data.json lacks the
carried usage, so a rebuild in place would rewrite the stage.
carried usage, so a rebuild in place would rewrite the stage. A copy that
lacks a file export reads, because the receipt does not bind it, is
refused by build_payload; the rebuild never reads the working directory's
annotations in its place.
"""
bundle = Path("publish") / RUN
with tempfile.TemporaryDirectory(prefix="freeze-gpt61sol-") as scratch:
Expand Down Expand Up @@ -435,6 +442,14 @@ def verify_annotation_amendments(
)


def verify_frozen(frozen_run: Path, annotations: Path) -> None:
"""After the freezer writes the snapshot: it copies the staged references
and their explanations byte for byte, so the frozen copies must be
release 20260929's pins and explanations."""
driver.verify_reference_pins(frozen_run, "frozen reference")
driver.verify_reference_explanations(annotations, "Frozen")


def freeze_amendments(stage: Path, destination: Path) -> None:
"""Commit the stage's wording amendments beside the adjudication record.

Expand Down Expand Up @@ -470,6 +485,14 @@ def main(argv: list[str] | None = None) -> None:

payload_path = stage / f"data-board{BOARD_MODELS}.json"
receipt = verify_receipt(stage, payload_path, args.tag)
# The receipt binds the stage only by hashes stored beside it; its pin,
# committed at HEAD, means a file the receipt binds can change after
# export, its receipt re-hashed to match, only with a visible commit.
driver.verify_release_receipt(stage, payload_path, args.tag)
# Readers of the stage's bundle (collect_audit, wrong_prediction_rows)
# would read an extra annotations file, and load_predictions would rewrite
# us/predictions.csv from a us/by_model/ folder.
driver.verify_bundle_files(stage / "publish" / RUN)
# GPT-6.1 Sol's run files are the committed pins' and its bundle rows are
# its run file's, every column: its cost, tokens and latency included.
driver.verify_new_model_inputs(stage)
Expand Down Expand Up @@ -506,10 +529,14 @@ def main(argv: list[str] | None = None) -> None:
source_run = stage / "publish" / RUN
source_us = source_run / "us"
verify_references(source_us, frozen_run, read_json(snapshot / "manifest.json"))
driver.verify_reference_explanations(source_run / "annotations", "Staged")
del payload, stats
rebuild_payload(stage, receipt, payload_path, base)
del base
staged_annotations = source_run / "annotations"
# The manifest decides which cases are judged and builds the
# parse-failure-only cases' annotations; it must be the bundle's.
driver.verify_manifest(stage)
verify_verdicts(stage)
rejudged = driver.rejudged_cases(stage)
verify_judge_provenance(stage, receipt, rejudged)
Expand All @@ -530,6 +557,9 @@ def main(argv: list[str] | None = None) -> None:
):
raise SystemExit(f"Incumbent prediction evidence changed: {model}")
del previous_rows
# Every column of the incumbents' rows, not only the answers the evidence
# hash covers: the response window and the usage summary read the rest.
driver.verify_incumbent_rows(stage)

evidence_paths = {}
for slug, model in NEW_MODELS.items():
Expand Down Expand Up @@ -656,8 +686,7 @@ def freeze_serving(destination: Path) -> None:
)
freezer.main()
freeze_amendments(stage, freezer.ANNOTATIONS_DEST)
# The freezer copies the staged references byte for byte; confirm it.
driver.verify_reference_pins(frozen_run, "frozen reference")
verify_frozen(frozen_run, freezer.ANNOTATIONS_DEST)
cache = ROOT / "app/.cache" / f"dashboard-data-{payload_hash[:16]}.json"
cache.parent.mkdir(parents=True, exist_ok=True)
shutil.copyfile(payload_path, cache)
Expand Down
32 changes: 23 additions & 9 deletions scripts/restate_gpt61sol_adjudications.py
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,18 @@
)


def reference_flag(flag: bool, waved: bool) -> tuple[bool, bool]:
"""The reference flag a record carries for a verdict whose own flag is
``flag``, where ``waved`` says whether a 2026-09-22 judge run flagged its
case: whether judge_reference_suspect is raised (by the verdict or by the
earlier run), and whether it names a judge_reference_suspect_source
(exactly when the earlier run alone raised it). A restated entry's top
level and the judge_previous item for the replaced verdict both follow
it, and the freeze's verify_restatements requires it of every re-opened
entry."""
return flag or waved, waved and not flag


def _put(entry: dict, key: str, value, before: tuple[str, ...] = ()) -> dict:
"""``entry`` with ``key`` set; a new key goes before the first of ``before``
the entry has, else last."""
Expand Down Expand Up @@ -163,15 +175,15 @@ def replaced_item(found: tuple[dict, dict], waved: bool) -> dict:
sidecar records it (``found`` is ``_bound_verdict``'s pair); ``waved``
says whether a 2026-09-22 run flagged its case."""
old, old_meta = found
old_flag = bool(old.get("reference_suspect"))
raised, sourced = reference_flag(bool(old.get("reference_suspect")), waved)
item = {
"judge_model": _judge(old_meta),
"judge_failure_source": old["case_failure_source"],
"judge_failure_subtype": old["case_failure_subtype"],
"judge_reference_suspect": old_flag or waved,
"judge_reference_suspect": raised,
"judged_on": _utc_day(old_meta["judged_at_utc"]),
}
if waved and not old_flag:
if sourced:
item["judge_reference_suspect_source"] = ITEM_FLAG_SOURCE
return item

Expand All @@ -188,15 +200,16 @@ def _restated(
item: dict,
day: str,
previous: list[dict],
waved: bool,
flag: bool,
sourced: bool,
) -> dict:
"""``entry`` naming the stage verdict ``top``, dated ``day``, with ``item``
(the replaced verdict) appended to ``previous``."""
(the replaced verdict) appended to ``previous``. Its flag names a source
when ``sourced`` (reference_flag): the entry's own wording where it has
one, else FLAG_SOURCE_EARLIER_RUN."""
restated = dict(entry)
for name, value in top.items():
restated = _put(restated, name, value, before=_trailing(restated))
if not (waved and not flag):
if not sourced:
restated.pop("judge_reference_suspect_source", None)
elif "judge_reference_suspect_source" not in restated:
restated = _put(
Expand Down Expand Up @@ -281,11 +294,12 @@ def restate_entries(
old_flag = bool(old.get("reference_suspect"))
item = replaced_item(replaced, waved)
flag = bool(verdict.get("reference_suspect"))
raised, sourced = reference_flag(flag, waved)
top = {
"judge_model": _judge(meta),
"judge_failure_source": verdict["case_failure_source"],
"judge_failure_subtype": verdict["case_failure_subtype"],
"judge_reference_suspect": flag or waved,
"judge_reference_suspect": raised,
}
day = _utc_day(meta["judged_at_utc"])
if day < item["judged_on"]:
Expand Down Expand Up @@ -327,7 +341,7 @@ def restate_entries(
f"{case}: the record does not name the seed verdict it "
f"replaces ({named}, dated {dated}, vs {item})"
)
restated = _restated(entry, top, item, day, base, waved, flag)
restated = _restated(entry, top, item, day, base, sourced)
if _names(restated, item):
raise SystemExit(
f"{case}: the re-judge repeats the seed verdict on the seed's own "
Expand Down
Loading
Loading