{"record":{"id":"ef4d566820d06471","repo":"Hmbown/CodeWhale","slug":"receipt-missing-required-field-s-join-m","errorCode":null,"errorMessage":"receipt missing required field(s): \" + \", \".join(missing)","messagePattern":"receipt missing required field\\(s\\): \" \\+ \", \"\\.join\\(missing\\)","errorType":"exception","errorClass":"PersistenceBacklogError","httpStatus":null,"severity":"error","filePath":"scripts/check-persistence-backlog-budget.py","lineNumber":144,"sourceCode":"        \"source_dirty\": bool(\n            run([\"git\", \"status\", \"--porcelain\", \"--untracked-files=normal\"])\n        ),\n        \"rustc_version\": run([\"rustc\", \"--version\"]),\n        \"cargo_version\": run([\"cargo\", \"--version\"]),\n        \"build_profile\": \"test\",\n        \"sample_count\": 1,\n    }\n\n\ndef validate_receipt(\n    receipt: dict[str, Any],\n    *,\n    expected_source: dict[str, Any] | None = None,\n    require_clean_source: bool = False,\n) -> None:\n    missing = [field for field in REQUIRED_RECEIPT_FIELDS if field not in receipt]\n    if missing:\n        raise PersistenceBacklogError(\n            \"receipt missing required field(s): \" + \", \".join(missing)\n        )\n    if receipt[\"document_kind\"] != RECEIPT_KIND:\n        raise PersistenceBacklogError(f\"receipt document_kind must be {RECEIPT_KIND}\")\n    if receipt[\"schema_version\"] != SCHEMA_VERSION:\n        raise PersistenceBacklogError(\"receipt schema_version changed\")\n    for field, expected in FIXTURE.items():\n        validate_frozen_field(field, receipt[field], expected)\n    if not isinstance(receipt[\"source_sha\"], str) or not SOURCE_SHA_PATTERN.fullmatch(\n        receipt[\"source_sha\"]\n    ):\n        raise PersistenceBacklogError(\"receipt source_sha must be an exact lowercase Git SHA\")\n    if type(receipt[\"source_dirty\"]) is not bool:\n        raise PersistenceBacklogError(\"receipt source_dirty must be boolean\")\n    for field, prefix in ((\"rustc_version\", \"rustc \"), (\"cargo_version\", \"cargo \")):\n        if not isinstance(receipt[field], str) or not receipt[field].startswith(prefix):\n            raise PersistenceBacklogError(f\"receipt {field} must be a version string\")\n    validate_frozen_field(\"build_profile\", receipt[\"build_profile\"], \"test\")","sourceCodeStart":126,"sourceCodeEnd":162,"githubUrl":"https://github.com/Hmbown/CodeWhale/blob/8880682c63083a91624de936797efa3ce9e498fd/scripts/check-persistence-backlog-budget.py#L126-L162","documentation":"Raised in validate_receipt (scripts/check-persistence-backlog-budget.py:142-146) when any of the 29 REQUIRED_RECEIPT_FIELDS (document_kind through limitations) is absent from the receipt JSON. The checker demands a complete, closed schema so partial receipts cannot silently pass the budget gate.","triggerScenarios":"Checking a receipt written by an older schema (missing newer fields like rss_*); a truncated or hand-built JSON; the Rust test failing to populate a field; a typo'd key in the emitter (e.g. \"final_version_applied\" vs \"finalVersionApplied\").","commonSituations":"Schema evolution where a field was added to REQUIRED_RECEIPT_FIELDS but old receipts linger; reusing a CI-cached receipt artifact; manual receipt construction for testing.","solutions":["Regenerate the receipt via scripts/measure-persistence-backlog.py so all current fields are emitted.","If you added a receipt field, also add it to the Rust test's receipt writer and to REQUIRED_RECEIPT_FIELDS in the same commit.","Delete stale receipt artifacts from CI caches so the fresh emitter output is always used."],"exampleFix":"// receipt (before, older schema)\n{ \"document_kind\": \"...\", \"schema_version\": 2 }\n// receipt (after, complete)\n{ \"document_kind\": \"...\", \"schema_version\": 2, /* ...all 29 required keys... */ \"limitations\": [\"...\"] }","handlingStrategy":"type-guard","validationCode":"missing = [f for f in REQUIRED_RECEIPT_FIELDS if f not in receipt]\nif missing:\n    raise SystemExit(f\"incomplete receipt, regenerate: missing {', '.join(missing)}\")","typeGuard":"def has_all_required_fields(receipt: dict) -> bool:\n    return all(field in receipt for field in REQUIRED_RECEIPT_FIELDS)","tryCatchPattern":"try:\n    validate_receipt(receipt)\nexcept PersistenceBacklogError as e:\n    if str(e).startswith(\"receipt missing required field\"):\n        # schema drift: regenerate instead of patching fields in\n        subprocess.run([sys.executable, \"scripts/measure-persistence-backlog.py\"], check=True)\n    raise","preventionTips":["Extend REQUIRED_RECEIPT_FIELDS and the Rust emitter in the same commit as any new field.","Purge cached receipt artifacts from CI between schema bumps.","Always generate receipts with the measure script rather than composing JSON by hand."],"tags":["validation","json","schema"],"backgroundTag":null,"analyzedSha":"8880682c63083a91624de936797efa3ce9e498fd","analyzedAt":"2026-08-16T11:31:27.956Z","schemaVersion":2},"datasetVersion":"2026-08-16T13:17:31.715Z"}