{"record":{"id":"2acbeefa1c0e8b57","repo":"Hmbown/CodeWhale","slug":"budget-schema-version-changed","errorCode":null,"errorMessage":"budget schema_version changed","messagePattern":"budget schema_version changed","errorType":"exception","errorClass":"PersistenceBacklogError","httpStatus":null,"severity":"error","filePath":"scripts/check-persistence-backlog-budget.py","lineNumber":246,"sourceCode":"            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}\")\n        observed = non_negative_integer(\n            baseline.get(field), f\"baseline_observation.{field}\"","sourceCodeStart":228,"sourceCodeEnd":264,"githubUrl":"https://github.com/Hmbown/CodeWhale/blob/8880682c63083a91624de936797efa3ce9e498fd/scripts/check-persistence-backlog-budget.py#L228-L264","documentation":"Raised by validate_budget() when budget['schema_version'] != 2 (SCHEMA_VERSION frozen in the checker). Budget and receipt schemas are version-frozen together, so a mismatch means the document was written for a different generation of the contract and every later field check would be unreliable. The same constant gates receipts (validate_receipt raises the parallel 'receipt schema_version changed'), so a schema bump invalidates both documents at once.","triggerScenarios":"Loading a budget whose schema_version is missing, 1, 3, or the string '2' - typically an old budget file checked out against a newer checker, or a budget produced by tooling from a different schema generation.","commonSituations":"Checking out an old scripts/persistence-backlog-budget.json against a new checker (or vice versa); a schema migration that bumped one document but not the other; hand-editing schema_version hoping to skip re-validation.","solutions":["Use the budget and checker from the same checkout: scripts/persistence-backlog-budget.json with schema_version 2","If the schema genuinely changed upstream, regenerate both the budget and the baseline receipt with the new tooling instead of hand-bumping the field","Run git status/log on scripts/*.json to detect a stale or mixed-version file"],"exampleFix":"// before (budget.json)\n\"document_kind\": \"codewhale.persistence_backlog_budget\",\n\"schema_version\": 1\n\n// after: regenerate the document with the current tooling\n\"document_kind\": \"codewhale.persistence_backlog_budget\",\n\"schema_version\": 2","handlingStrategy":"validation","validationCode":"def schema_version_ok(doc: dict, expected: int = 2) -> bool:\n    v = doc.get(\"schema_version\")\n    return isinstance(v, int) and not isinstance(v, bool) and v == expected","typeGuard":null,"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","schema-version","python","ci-gate","versioning"],"backgroundTag":null,"analyzedSha":"8880682c63083a91624de936797efa3ce9e498fd","analyzedAt":"2026-08-16T11:31:27.956Z","schemaVersion":2},"datasetVersion":"2026-08-16T13:17:31.715Z"}