Skip to content

Commit 5db2fde

Browse files
MaxGhenisclaude
andauthored
Republish with unified 20-model failure-audit annotations (20260707c) (#113)
* Republish with unified 20-model failure-audit annotations (20260707c) One coherent Codex audit of every wrong cell across all 20 models against the corrected narratives: 889 cases, 5,351 row annotations. Covers GPT-5.5's default-effort rerun for the first time and retires annotations that quoted the pre-correction narratives. Scores, predictions, and weights byte-identical to 20260707b. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * Gate publishes on judge-annotation coverage for wrong answers validate_country_payload gains require_failure_annotations: every scenarioPredictions leaf that is wrong by the failure audit's own definition (thresholdScore < 100) must carry a judged failureSource. publish-dashboard and the validate-dashboard CLI enforce it; plain exports stay structural because fold-board staging legitimately precedes the audit. The 20260707b payload shipped GPT-5.5 wrong answers with no judge annotation because nothing asserted coverage. publish_dashboard now refuses that payload with 46 errors while the unified-audit payload (20260707c) passes clean. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> --------- Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
1 parent d286462 commit 5db2fde

7 files changed

Lines changed: 5305 additions & 5235 deletions

File tree

annotations/us_full_run_20260612_policyengine_4_16_1_populace/us_audit_row_annotations.csv

Lines changed: 5175 additions & 5217 deletions
Large diffs are not rendered by default.

app/src/data.artifact.json

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
{
22
"version": 1,
33
"repo": "PolicyEngine/policybench",
4-
"tag": "dashboard-data-20260707b",
4+
"tag": "dashboard-data-20260707c",
55
"asset": "dashboard-data.json",
6-
"url": "https://github.com/PolicyEngine/policybench/releases/download/dashboard-data-20260707b/dashboard-data.json",
7-
"sha256": "a9b8bee0802ed9446e8c6a1d954afbe0ddbe478803b9041da8cfa2f0f382f43d",
8-
"bytes": 57597347
6+
"url": "https://github.com/PolicyEngine/policybench/releases/download/dashboard-data-20260707c/dashboard-data.json",
7+
"sha256": "0860f8ebab3853441ee91c7283dc465434e52e9c2266975af7d58baa712d7f0a",
8+
"bytes": 57737111
99
}

paper/snapshot/20260501/manifest.json

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -20,9 +20,9 @@
2020
],
2121
"live_dashboard_artifact": {
2222
"asset": "dashboard-data.json",
23-
"bytes": 57597347,
24-
"derivation": "PolicyBench dataset v1.1, 20-model board (dashboard-data-20260707) with corrected reference-derivation narratives only. 177 Medicaid narratives are regenerated with the engine's eligibility category injected as authoritative grounding (29 had described 65+ people via the ACA adult expansion), and 3 narratives that still described pre-v1.1 reference values (scenario_008/scenario_100 Head Start, scenario_056 NJ state tax) are regenerated against the current engine. Every score, prediction leaf, modelStats entry, and globalWeights value is byte-identical to dashboard-data-20260707; the only changed payload fields are referenceExplanation (180 cells x 20 model leaves). Generator grounding mechanism and regenerated annotations landed in PR #110; the #107 audit adjudicated all 14 reference-suspect flags with zero real reference bugs.",
25-
"notes": "Narrative-correction republish (same pattern as dashboard-data-20260702b): dataset version unchanged (v1.1), no selector changes. claude-fable-5 usage fields remain artifact-level values.",
23+
"bytes": 57737111,
24+
"derivation": "PolicyBench dataset v1.1, 20-model board with the unified failure-audit annotations. Every wrong cell across all 20 models is Codex-judged in one coherent pass against the corrected reference-derivation narratives (889 cases, 636 substantive verdicts, 5,351 row annotations) \u2014 replacing the patchwork of June and per-batch audits, covering GPT-5.5's default-effort rerun for the first time, and retiring annotations that quoted the pre-correction narratives. Every score, prediction leaf, modelStats entry, and globalWeights value is byte-identical to dashboard-data-20260707b; the only changed payload fields are the annotation classes (annotation, failureSource, failureSubtype, caseAnnotation, caseFailureSources, caseFailureSubtypes).",
25+
"notes": "Annotation republish (pattern of 20260702b/20260707b): dataset version unchanged (v1.1), no selector changes. claude-fable-5 usage fields remain artifact-level values. Fresh audit flags 10 reference-suspect cases; 5 were previously adjudicated reference-correct (#107), 5 new are under adjudication.",
2626
"population_weights": {
2727
"sha256": "0b3b96b8247477631510371b9c3b021a18a535e8ebbf8c73a8aa67cff3110cd4",
2828
"source": "committed June artifact policybench/population_weights.json (unchanged)"
@@ -32,9 +32,9 @@
3232
"policyengine_us_version": "1.755.4",
3333
"policyengine_version": "4.16.1"
3434
},
35-
"sha256": "a9b8bee0802ed9446e8c6a1d954afbe0ddbe478803b9041da8cfa2f0f382f43d",
36-
"tag": "dashboard-data-20260707b",
37-
"url": "https://github.com/PolicyEngine/policybench/releases/download/dashboard-data-20260707b/dashboard-data.json"
35+
"sha256": "0860f8ebab3853441ee91c7283dc465434e52e9c2266975af7d58baa712d7f0a",
36+
"tag": "dashboard-data-20260707c",
37+
"url": "https://github.com/PolicyEngine/policybench/releases/download/dashboard-data-20260707c/dashboard-data.json"
3838
},
3939
"live_dashboard_note": "Two artifacts are tracked. published_dashboard_artifact pins the manuscript's frozen record: it equals the combined export of the source run data.json files listed under source_run_artifacts, byte for byte. live_dashboard_artifact is the release asset the committed pointer app/src/data.artifact.json references; it starts from the frozen export and may gain additive updates (injected metrics, newly benchmarked models) without changing the frozen models' scores. Every publish-dashboard run must update live_dashboard_artifact (tag, sha256, bytes, derivation) in the same commit as the pointer.",
4040
"model_response_date": "2026-06-12 to 2026-06-13",

policybench/cli.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1414,7 +1414,9 @@ def main():
14141414
payload = json_module.loads(payload_path.read_text(encoding="utf-8"))
14151415
except json_module.JSONDecodeError as exc:
14161416
raise SystemExit(f"{payload_path} is not valid JSON: {exc}") from exc
1417-
errors = validate_dashboard_payload(payload)
1417+
# The CLI check is the pre-publish gate, so it matches
1418+
# publish-dashboard strictness: wrong answers need judge annotations.
1419+
errors = validate_dashboard_payload(payload, require_failure_annotations=True)
14181420
if errors:
14191421
for error in errors:
14201422
print(f"ERROR: {error}")

policybench/dashboard_schema.py

Lines changed: 49 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -53,8 +53,18 @@ def _is_finite_number(value: Any) -> bool:
5353
return _is_number(value) and math.isfinite(value)
5454

5555

56-
def validate_country_payload(bench: Any, *, country: str | None = None) -> list[str]:
57-
"""Validate one country's bench payload. Returns a list of errors."""
56+
def validate_country_payload(
57+
bench: Any,
58+
*,
59+
country: str | None = None,
60+
require_failure_annotations: bool = False,
61+
) -> list[str]:
62+
"""Validate one country's bench payload. Returns a list of errors.
63+
64+
``require_failure_annotations`` additionally requires every wrong answer
65+
(thresholdScore < 100) to carry a judged ``failureSource``. Publishing
66+
enforces it; staged exports that precede the failure audit do not.
67+
"""
5868
if not isinstance(bench, dict):
5969
return [f"bench payload must be an object, got {type(bench).__name__}"]
6070

@@ -171,6 +181,24 @@ def validate_country_payload(bench: Any, *, country: str | None = None) -> list[
171181
f"{variable}.{model}: groundTruth must be a finite "
172182
f"number, got {record['groundTruth']!r}"
173183
)
184+
# Every wrong answer must carry a judged failure
185+
# annotation. GPT-5.5's default-effort rerun shipped
186+
# unannotated because nothing asserted coverage — the
187+
# publish gate now fails instead of shipping unexplained
188+
# misses. "Wrong" matches the failure audit's own
189+
# definition: thresholdScore below 100.
190+
threshold = record.get("thresholdScore")
191+
if (
192+
require_failure_annotations
193+
and _is_finite_number(threshold)
194+
and float(threshold) < 100
195+
and not record.get("failureSource")
196+
):
197+
errors.append(
198+
f"{prefix}.scenarioPredictions.{scenario_id}."
199+
f"{variable}.{model}: wrong answer has no judged "
200+
f"failureSource annotation"
201+
)
174202

175203
failure_modes = bench.get("failureModes")
176204
if not isinstance(failure_modes, dict) or not {
@@ -184,7 +212,9 @@ def validate_country_payload(bench: Any, *, country: str | None = None) -> list[
184212
return errors
185213

186214

187-
def validate_dashboard_payload(payload: Any) -> list[str]:
215+
def validate_dashboard_payload(
216+
payload: Any, *, require_failure_annotations: bool = False
217+
) -> list[str]:
188218
"""Validate the combined app payload. Returns a list of errors."""
189219
if not isinstance(payload, dict):
190220
return [f"payload must be an object, got {type(payload).__name__}"]
@@ -213,13 +243,26 @@ def validate_dashboard_payload(payload: Any) -> list[str]:
213243

214244
for country, bench in countries.items():
215245
if country in COUNTRY_CODES:
216-
errors.extend(validate_country_payload(bench, country=country))
246+
errors.extend(
247+
validate_country_payload(
248+
bench,
249+
country=country,
250+
require_failure_annotations=require_failure_annotations,
251+
)
252+
)
217253

218254
return errors
219255

220256

221-
def assert_valid_dashboard_payload(payload: Any, *, source: str = "payload") -> None:
222-
errors = validate_dashboard_payload(payload)
257+
def assert_valid_dashboard_payload(
258+
payload: Any,
259+
*,
260+
source: str = "payload",
261+
require_failure_annotations: bool = False,
262+
) -> None:
263+
errors = validate_dashboard_payload(
264+
payload, require_failure_annotations=require_failure_annotations
265+
)
223266
if errors:
224267
raise DashboardValidationError(source, errors)
225268

policybench/publish_dashboard.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,9 @@ def publish_dashboard(
8484

8585
raw = source_path.read_bytes()
8686
payload = json.loads(raw)
87-
assert_valid_dashboard_payload(payload, source=str(source_path))
87+
assert_valid_dashboard_payload(
88+
payload, source=str(source_path), require_failure_annotations=True
89+
)
8890

8991
digest = hashlib.sha256(raw).hexdigest()
9092
pointer = build_pointer(

tests/test_dashboard_schema.py

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,71 @@ def test_no_tools_condition_required():
106106
assert any("no_tools" in error for error in errors)
107107

108108

109+
def make_wrong_leaf(**extra) -> dict:
110+
return {"prediction": 100.0, "groundTruth": 90.0, "thresholdScore": 0.0, **extra}
111+
112+
113+
def test_wrong_answer_without_failure_annotation_fails_publish_gate():
114+
# The gap class shipped in 20260707b: GPT-5.5's default-effort rerun had
115+
# wrong answers with no judged failureSource. Publishing must fail.
116+
bench = make_bench()
117+
bench["scenarioPredictions"]["scenario_001"]["snap"]["model-a"] = make_wrong_leaf()
118+
errors = validate_country_payload(
119+
bench, country="us", require_failure_annotations=True
120+
)
121+
assert errors == [
122+
"countries.us.scenarioPredictions.scenario_001.snap.model-a: "
123+
"wrong answer has no judged failureSource annotation"
124+
]
125+
126+
127+
def test_wrong_answer_without_failure_annotation_allowed_in_staged_export():
128+
# Fold-board staging exports precede the failure audit, so the default
129+
# (non-publish) validation stays structural.
130+
bench = make_bench()
131+
bench["scenarioPredictions"]["scenario_001"]["snap"]["model-a"] = make_wrong_leaf()
132+
assert validate_country_payload(bench, country="us") == []
133+
134+
135+
def test_wrong_answer_with_failure_annotation_passes_publish_gate():
136+
bench = make_bench()
137+
bench["scenarioPredictions"]["scenario_001"]["snap"]["model-a"] = make_wrong_leaf(
138+
failureSource="model",
139+
failureSubtype="calculation_error",
140+
annotation="The model omitted the standard deduction.",
141+
)
142+
errors = validate_country_payload(
143+
bench, country="us", require_failure_annotations=True
144+
)
145+
assert errors == []
146+
147+
148+
def test_correct_answer_needs_no_failure_annotation():
149+
bench = make_bench()
150+
bench["scenarioPredictions"]["scenario_001"]["snap"]["model-a"] = {
151+
"prediction": 90.0,
152+
"groundTruth": 90.0,
153+
"thresholdScore": 100.0,
154+
}
155+
errors = validate_country_payload(
156+
bench, country="us", require_failure_annotations=True
157+
)
158+
assert errors == []
159+
160+
161+
def test_dashboard_payload_threads_annotation_requirement():
162+
payload = make_payload()
163+
payload["countries"]["us"]["scenarioPredictions"]["scenario_001"]["snap"][
164+
"model-a"
165+
] = make_wrong_leaf()
166+
assert validate_dashboard_payload(payload) == []
167+
errors = validate_dashboard_payload(payload, require_failure_annotations=True)
168+
assert errors == [
169+
"countries.us.scenarioPredictions.scenario_001.snap.model-a: "
170+
"wrong answer has no judged failureSource annotation"
171+
]
172+
173+
109174
def test_nan_score_reported():
110175
bench = make_bench()
111176
bench["modelStats"][0]["score"] = float("nan")

0 commit comments

Comments
 (0)