{"record":{"id":"7a2122f865ec007b","repo":"paperclipai/paperclip","slug":"missing-campaign-coverage-flag","errorCode":null,"errorMessage":"Missing campaign coverage flag","messagePattern":"Missing campaign coverage flag","errorType":"validation","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"scripts/evals-hub/build.py","lineNumber":58,"sourceCode":"    return html.escape(url, quote=True)\n\n\ndef display_date(value):\n    date = datetime.fromisoformat(value.replace(\"Z\", \"+00:00\"))\n    if date.tzinfo is None:\n        raise ValueError(\"History date must include a timezone\")\n    return date.astimezone(timezone.utc).strftime(\"%d %b %Y · %H:%M UTC\")\n\n\ndef summarize(history, kind):\n    prefix, schema = SYSTEMS[kind]\n    campaign = latest(history, schema)\n    totals = campaign[\"totals\"] if kind == \"runner\" else campaign\n    passed, selected = totals[\"passed\"], totals[\"selected\"]\n    if any(type(n) is not int for n in (passed, selected)) or not 0 <= passed <= selected:\n        raise ValueError(\"Invalid campaign counts\")\n    if type(campaign.get(\"complete\")) is not bool:\n        raise ValueError(\"Missing campaign coverage flag\")\n    revision = campaign.get(\"reportRevision\", {})\n    if revision and not isinstance(revision.get(\"sourceGeneratedAt\"), str):\n        raise ValueError(\"Report refresh has no reliable measurement date\")\n    measured = revision.get(\"sourceGeneratedAt\", campaign[\"generatedAt\"])\n    return {\n        \"url\": public_report(campaign[\"publicUrl\"], prefix),\n        \"date\": html.escape(display_date(measured)),\n        \"counts\": f\"{passed} / {selected} selected cases passed\",\n        \"coverage\": \"Complete campaign\" if campaign[\"complete\"] else \"Partial campaign\",\n        \"id\": html.escape(revision.get(\"sourceCampaignId\", campaign[\"campaignId\"])),\n    }\n\n\ndef main():\n    parser = argparse.ArgumentParser(description=__doc__)\n    parser.add_argument(\"--output\", type=Path, required=True)\n    parser.add_argument(\"--history-dir\", type=Path, help=\"Use saved public history JSON files for offline verification\")\n    parser.add_argument(\"--docs-ref\", default=\"master\", help=\"Git ref containing doc/evals.md\")","sourceCodeStart":40,"sourceCodeEnd":76,"githubUrl":"https://github.com/paperclipai/paperclip/blob/3f1d897a7c018d76563a21c6e39c3c9b03933622/scripts/evals-hub/build.py#L40-L76","documentation":"Every campaign must carry a boolean `complete` flag indicating full vs partial coverage, which the page renders as 'Complete campaign' or 'Partial campaign'. If the key is missing or not a bool, summarize() refuses to guess and raises ValueError.","triggerScenarios":"A campaign entry lacking the `complete` key, or storing it as a string ('true'), int (1/0), or null — e.g. older feeds predating the flag, or a producer serializing the wrong type.","commonSituations":"Old history fixtures generated before the coverage flag was introduced; publisher bug omitting the field for resumed campaigns; schema drift between feed versions.","solutions":["Re-publish the campaign with an explicit boolean `complete` field.","Fix the producer to always set complete (true/false) on every campaign record.","If consuming a legacy feed, migrate it: add `complete` derived from the run's selection coverage.","Update offline fixtures in --history-dir to include complete: true/false."],"exampleFix":"# before\n{\"campaignId\": \"c-1\", \"generatedAt\": \"...\"}  # no complete flag\n# after\n{\"campaignId\": \"c-1\", \"generatedAt\": \"...\", \"complete\": true}","handlingStrategy":"validation","validationCode":"def has_complete_flag(campaign):\n    return type(campaign.get(\"complete\")) is bool","typeGuard":"def has_bool_complete(campaign):\n    return type(campaign.get(\"complete\")) is bool","tryCatchPattern":"try:\n    values = summarize(history, kind)\nexcept ValueError as e:\n    if str(e) == \"Missing campaign coverage flag\":\n        log.error(\"campaign missing boolean `complete`; migrate feed before building\")\n        raise\n    raise","preventionTips":["Always set complete: true/false on every campaign record at publish time.","Reject string/number booleans in the producer's serialization layer.","Migrate legacy feeds to add the flag before pointing build.py at them.","Include `complete` in fixture templates for tests."],"tags":["validation","missing-field","python","coverage"],"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"}