{"record":{"id":"c0d242dbc1a0db3b","repo":"Hmbown/CodeWhale","slug":"budget-document-kind-must-be-budget-kind","errorCode":null,"errorMessage":"budget document_kind must be {BUDGET_KIND}","messagePattern":"budget document_kind must be (.+?)","errorType":"exception","errorClass":"PersistenceBacklogError","httpStatus":null,"severity":"error","filePath":"scripts/check-persistence-backlog-budget.py","lineNumber":244,"sourceCode":"    if receipt[\"rss_supported\"]:\n        for field in rss_fields:\n            non_negative_integer(receipt[field], field)\n        before = receipt[\"rss_before_bytes\"]\n        if receipt[\"rss_during_delta_bytes\"] != max(\n            0, receipt[\"rss_during_bytes\"] - before\n        ):\n            raise PersistenceBacklogError(\"rss_during_delta_bytes is inconsistent\")\n        if receipt[\"rss_after_delta_bytes\"] != max(\n            0, receipt[\"rss_after_bytes\"] - before\n        ):\n            raise PersistenceBacklogError(\"rss_after_delta_bytes is inconsistent\")\n    elif any(receipt[field] is not None for field in rss_fields):\n        raise PersistenceBacklogError(\"unsupported RSS fields must be null\")\n\n\ndef validate_budget(budget: dict[str, Any]) -> None:\n    if budget.get(\"document_kind\") != BUDGET_KIND:\n        raise PersistenceBacklogError(f\"budget document_kind must be {BUDGET_KIND}\")\n    if budget.get(\"schema_version\") != SCHEMA_VERSION:\n        raise PersistenceBacklogError(\"budget schema_version changed\")\n    fixture = budget.get(\"fixture\")\n    if not isinstance(fixture, dict) or set(fixture) != set(FIXTURE):\n        raise PersistenceBacklogError(\"budget fixture no longer matches the frozen workload\")\n    for field, expected in FIXTURE.items():\n        if type(fixture[field]) is not type(expected) or fixture[field] != expected:\n            raise PersistenceBacklogError(\n                f\"budget fixture.{field} must remain {expected!r}\"\n            )\n    if budget.get(\"baseline_receipt\") != BASELINE_RECEIPT_REFERENCE:\n        raise PersistenceBacklogError(\"budget baseline_receipt path changed\")\n    ceilings = budget.get(\"ceilings\")\n    baseline = budget.get(\"baseline_observation\")\n    if not isinstance(ceilings, dict) or not isinstance(baseline, dict):\n        raise PersistenceBacklogError(\"budget needs ceilings and baseline_observation objects\")\n    for field in CEILING_FIELDS:\n        ceiling = non_negative_integer(ceilings.get(field), f\"ceilings.{field}\")","sourceCodeStart":226,"sourceCodeEnd":262,"githubUrl":"https://github.com/Hmbown/CodeWhale/blob/8880682c63083a91624de936797efa3ce9e498fd/scripts/check-persistence-backlog-budget.py#L226-L262","documentation":"Raised by validate_budget() when the loaded budget JSON's document_kind is not the exact discriminator 'codewhale.persistence_backlog_budget' (BUDGET_KIND). The discriminator guarantees the checker validates the intended document class and never silently accepts a receipt, a differently-versioned budget, or an unrelated JSON file passed via --budget.","triggerScenarios":"Running scripts/check-persistence-backlog-budget.py --budget <file> where document_kind is missing, misspelled, or holds another kind such as 'codewhale.persistence_backlog_receipt' (the receipt discriminator).","commonSituations":"Passing the receipt file to --budget by mistake; a schema migration renaming the document kind without regenerating budgets; copy-pasting a budget template that kept a placeholder kind.","solutions":["Run the checker without --budget so it loads the default scripts/persistence-backlog-budget.json","In the custom budget file, set document_kind to exactly 'codewhale.persistence_backlog_budget'","Check for whitespace, case, or trailing-character differences when copying the kind string"],"exampleFix":"// before (custom-budget.json)\n\"document_kind\": \"persistence_backlog_budget\"\n\n// after\n\"document_kind\": \"codewhale.persistence_backlog_budget\"","handlingStrategy":"validation","validationCode":"BUDGET_KIND = \"codewhale.persistence_backlog_budget\"\n\ndef is_budget_doc(doc: dict) -> bool:\n    return isinstance(doc, dict) and doc.get(\"document_kind\") == BUDGET_KIND","typeGuard":"from typing import TypeGuard\n\ndef is_budget_doc(doc: object) -> TypeGuard[dict]:\n    return isinstance(doc, dict) and doc.get(\"document_kind\") == \"codewhale.persistence_backlog_budget\"","tryCatchPattern":null,"preventionTips":["Load budgets from the repo checkout (scripts/persistence-backlog-budget.json) instead of hand-maintained copies","Validate budget JSON with python -m json.tool and a key-set check before running the checker","Change FIXTURE/SCHEMA_VERSION in the checker only together with regenerating budget and baseline receipt in one commit"],"tags":["json","document-discriminator","python","ci-gate","configuration"],"backgroundTag":null,"analyzedSha":"8880682c63083a91624de936797efa3ce9e498fd","analyzedAt":"2026-08-16T11:31:27.956Z","schemaVersion":2},"datasetVersion":"2026-08-16T13:17:31.715Z"}