{"record":{"id":"bfd599964ce693c9","repo":"nextlevelbuilder/ui-ux-pro-max-skill","slug":"invalid-decision-rule-json","errorCode":null,"errorMessage":"invalid decision-rule JSON: {}","messagePattern":"invalid decision-rule JSON: (.+?)","errorType":"validation","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"src/ui-ux-pro-max/scripts/reasoning_contract.py","lineNumber":72,"sourceCode":"    for condition, signals in CONDITION_SIGNALS.items()\n}\n\n\ndef _object_without_duplicates(pairs):\n    result = {}\n    for key, value in pairs:\n        if key in result:\n            raise ValueError(\"duplicate decision-rule key: {}\".format(key))\n        result[key] = value\n    return result\n\n\ndef parse_decision_rules(raw):\n    \"\"\"Parse the canonical condition -> action-array representation.\"\"\"\n    try:\n        rules = json.loads(raw or \"{}\", object_pairs_hook=_object_without_duplicates)\n    except json.JSONDecodeError as error:\n        raise ValueError(\"invalid decision-rule JSON: {}\".format(error)) from error\n    if not isinstance(rules, dict):\n        raise ValueError(\"decision rules must be a JSON object\")\n    for condition, actions in rules.items():\n        if condition not in ALLOWED_CONDITIONS:\n            raise ValueError(\"unknown decision-rule condition: {}\".format(condition))\n        if not isinstance(actions, list) or not actions:\n            raise ValueError(\"{} must map to a non-empty action array\".format(condition))\n        for action in actions:\n            _validate_action(action)\n        if len(actions) != len(set(actions)):\n            raise ValueError(\"{} contains duplicate actions\".format(condition))\n    return rules\n\n\ndef _validate_action(action):\n    if not isinstance(action, str) or \":\" not in action:\n        raise ValueError(\"action must use a known prefix: {}\".format(action))\n    prefix, value = action.split(\":\", 1)","sourceCodeStart":54,"sourceCodeEnd":90,"githubUrl":"https://github.com/nextlevelbuilder/ui-ux-pro-max-skill/blob/a38d04c3d5c298c851dbe5e6ee1965ee3de42cb5/src/ui-ux-pro-max/scripts/reasoning_contract.py#L54-L90","documentation":"parse_decision_rules wraps json.loads and converts any json.JSONDecodeError into this ValueError with the underlying parse error appended. The decision-rule grammar only accepts well-formed JSON objects, so trailing commas, single quotes, comments, or truncation fail here before semantic validation begins.","triggerScenarios":"parse_decision_rules(raw) with raw like `{if_mobile: [...]}` (unquoted key), `{\"a\": [1,],}` (trailing comma), a string truncated by a CSV field limit, or None passed through as '{}' only when raw is falsy — a non-empty malformed string still hits json.loads.","commonSituations":"Writing rule JSON inline in a CSV cell and forgetting quotes around condition keys; shell heredocs mangling quotes; spreadsheet export truncating long cells.","solutions":["Copy the JSON string into a linter or `python3 -m json.tool` to find the exact syntax offset reported in the message.","Quote all keys and string values with double quotes; remove trailing commas and comments.","If the payload comes from a CSV column, check the cell was not truncated or line-wrapped on import."],"exampleFix":"// before\n{'if_mobile': ['mode:dark'],}  // single quotes + trailing comma\n\n// after\n{\"if_mobile\": [\"mode:dark\"]}","handlingStrategy":"validation","validationCode":"import json\ntry:\n    candidate = json.loads(raw)\nexcept json.JSONDecodeError as exc:\n    raise ValueError(f\"payload is not valid JSON at line {exc.lineno} col {exc.colno}: {exc.msg}\") from exc\n# only then hand to parse_decision_rules for contract checks","typeGuard":null,"tryCatchPattern":"try:\n    rules = rc.parse_decision_rules(raw)\nexcept ValueError as exc:\n    if 'invalid decision-rule JSON' in str(exc):\n        show_json_syntax_error(raw, str(exc))  # message embeds json's own position info\n    raise","preventionTips":["Author rule JSON with json.dumps from Python data, never by string building.","Pipe hand-written payloads through `python3 -m json.tool` before saving.","Watch CSV-hosted JSON for truncation on import (quote fields, raise cell length limits)."],"tags":["json","syntax","decision-rules","parsing"],"backgroundTag":null,"analyzedSha":"a38d04c3d5c298c851dbe5e6ee1965ee3de42cb5","analyzedAt":"2026-08-14T18:51:02.321Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}