{"record":{"id":"29373b24acd82526","repo":"paperclipai/paperclip","slug":"unsupported-history-schema","errorCode":null,"errorMessage":"Unsupported history schema","messagePattern":"Unsupported history schema","errorType":"exception","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"scripts/evals-hub/build.py","lineNumber":21,"sourceCode":"import argparse\nfrom datetime import datetime, timezone\nimport html\nimport json\nfrom pathlib import Path\nfrom string import Template\nfrom urllib.parse import quote, urlsplit\nfrom urllib.request import urlopen\n\nORIGIN = \"https://d1p6rlowie26tp.cloudfront.net\"\nSYSTEMS = {\n    \"runner\": (\"runner-protocol-evals\", \"paperclip.runner-protocol-eval.history/v1\"),\n    \"product\": (\"runner-e2e\", \"paperclip.runner-e2e.history/v1\"),\n}\n\n\ndef latest(history, schema):\n    if history.get(\"schema\") != schema:\n        raise ValueError(\"Unsupported history schema\")\n    campaign_id = history.get(\"latestCampaignId\")\n    if not campaign_id:\n        raise ValueError(\"History has no latest campaign\")\n    candidates = [c for c in history[\"campaigns\"] if\n                  c[\"campaignId\"] == campaign_id or\n                  c.get(\"reportRevision\", {}).get(\"sourceCampaignId\") == campaign_id]\n    if not candidates:\n        raise ValueError(\"Latest campaign is absent from history\")\n    # Report refreshes replace presentation, never the measurement date.\n    return max(candidates, key=lambda c: c.get(\"reportRevision\", {}).get(\"renderedAt\", c[\"generatedAt\"]))\n\n\ndef public_report(url, prefix):\n    parsed = urlsplit(url)\n    if (parsed.scheme != \"https\" or parsed.netloc != urlsplit(ORIGIN).netloc\n            or not parsed.path.startswith(f\"/{prefix}/campaigns/\")\n            or parsed.query or parsed.fragment):\n        raise ValueError(\"Unexpected public report URL\")","sourceCodeStart":3,"sourceCodeEnd":39,"githubUrl":"https://github.com/paperclipai/paperclip/blob/3f1d897a7c018d76563a21c6e39c3c9b03933622/scripts/evals-hub/build.py#L3-L39","documentation":"build.py consumes a public history JSON feed and validates its `schema` field against the expected schema string for the system kind (`paperclip.runner-protocol-eval.history/v1` or `paperclip.runner-e2e.history/v1`). A mismatch means the feed was produced by an unknown/newer/older writer, so the script refuses to guess the shape and raises ValueError in latest().","triggerScenarios":"Calling latest() (via summarize) with a history dict whose `schema` field is missing or does not equal the expected v1 schema for that kind — e.g. hand-edited history JSON, a feed upgraded to /v2, or swapping runner history with product history.","commonSituations":"Upstream feed format bump after a deploy; offline fixture files in --history-dir saved from an older format; passing the wrong prefix's history file for a kind.","solutions":["Regenerate or re-download the history feed so it carries the expected v1 schema string.","If the feed intentionally moved to a new schema version, update SYSTEMS in build.py to accept it and adapt the parser.","When using --history-dir, refresh the saved {prefix}-history.json fixture from the live origin.","Check you are feeding the right file: runner-protocol-evals history for kind 'runner', runner-e2e history for kind 'product'."],"exampleFix":"# before\nhistory = {\"schema\": \"paperclip.runner-e2e.history/v2\", ...}\nsummarize(history, \"product\")  # ValueError: Unsupported history schema\n# after\nhistory = {\"schema\": \"paperclip.runner-e2e.history/v1\", ...}  # matches SYSTEMS[\"product\"][1]","handlingStrategy":"validation","validationCode":"def ensure_schema(history, expected):\n    if history.get(\"schema\") != expected:\n        raise ValueError(f\"history schema {history.get('schema')!r} != expected {expected!r}\")","typeGuard":"def is_v1_history(history, schema):\n    return isinstance(history, dict) and history.get(\"schema\") == schema","tryCatchPattern":"try:\n    values = summarize(history, kind)\nexcept ValueError as e:\n    if str(e) == \"Unsupported history schema\":\n        history = revalidate_and_migrate(history)  # re-fetch or migrate to v1\n        values = summarize(history, kind)\n    else:\n        raise","preventionTips":["Pin the feed version: check the schema field immediately after fetching history.json.","Re-download fixtures when the upstream writer is upgraded.","Keep runner and product history files separated by prefix; never swap them.","Add a CI check that published history schema matches SYSTEMS values."],"tags":["validation","schema","python","feed"],"backgroundTag":"schema-validation-failed","analyzedSha":"3f1d897a7c018d76563a21c6e39c3c9b03933622","analyzedAt":"2026-09-18T08:03:59.046Z","contentChangedAt":"2026-09-18T08:03:59.046Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}