{"record":{"id":"a6d94d51971790e9","repo":"affaan-m/ECC","slug":"at-least-one-genre-spec-is-required","errorCode":null,"errorMessage":"at least one genre spec is required","messagePattern":"at least one genre spec is required","errorType":"validation","errorClass":"ContractError","httpStatus":null,"severity":"error","filePath":"skills/taste-application/scripts/tasteforge/contract.py","lineNumber":120,"sourceCode":"    if not stat.S_ISDIR(metadata.st_mode):\n        raise ContractError(\"output bundle root must be a directory\")\n    pending = [root]\n    while pending:\n        directory = pending.pop()\n        with os.scandir(directory) as entries:\n            for entry in entries:\n                if entry.is_symlink():\n                    raise ContractError(f\"output bundle contains a symlink: {entry.path}\")\n                if entry.is_dir(follow_symlinks=False):\n                    pending.append(Path(entry.path))\n                elif not entry.is_file(follow_symlinks=False):\n                    raise ContractError(f\"output bundle contains a special file: {entry.path}\")\n\n\ndef validate_genre_specs(specs: list[dict[str, Any]]) -> None:\n    \"\"\"Require complete, semantically distinct numbered genre specs.\"\"\"\n    if not specs:\n        raise ContractError(\"at least one genre spec is required\")\n    numbers = [spec.get(\"number\") for spec in specs]\n    if len(numbers) != len(set(numbers)):\n        raise ContractError(\"genre numbers must be distinct\")\n\n    fingerprints = [spec.get(\"style_fingerprint\") for spec in specs]\n    signatures = [_semantic_signature(spec) for spec in specs]\n    if len(fingerprints) != len(set(fingerprints)) or len(signatures) != len(set(signatures)):\n        raise ContractError(\"genre references collapsed into a generic style; distinct specs required\")\n\n    for spec in specs:\n        if spec.get(\"dry_run\") is not True:\n            raise ContractError(f\"genre {spec.get('number')} crosses the dry-run boundary\")\n        measured = spec.get(\"measured_features\")\n        if measured is not None:\n            if not isinstance(measured, dict):\n                raise ContractError(f\"genre {spec.get('number')} has invalid measured evidence\")\n            _validate_numeric_evidence(measured, label=f\"genre {spec.get('number')} evidence\")\n            total_duration = measured.get(\"total_duration\")","sourceCodeStart":102,"sourceCodeEnd":138,"githubUrl":"https://github.com/affaan-m/ECC/blob/8321021c54d670126ce3b2969d5deb880b4b0c2a/skills/taste-application/scripts/tasteforge/contract.py#L102-L138","documentation":"validate_genre_specs requires a non-empty list of genre spec dictionaries; each spec defines a numbered style target. An empty (or missing) list cannot satisfy the 'at least one distinct genre' contract, so validation fails early with this error.","triggerScenarios":"Calling validate_bundle/validate_genre_specs with specs=[] or None, or a config file whose genre list was never populated.","commonSituations":"Template config with an empty genres key, YAML/JSON list accidentally emptied during editing, a script path where specs are read from a file that exists but has no entries, or the CLI flag for genres omitted so an empty default is used.","solutions":["Provide at least one complete genre spec with number, style_fingerprint, and references","Fix the config file so the genre list is populated","If genres come from CLI flags/args, pass the required genre arguments explicitly","Check that the spec-loading code isn't filtering out all entries before validation"],"exampleFix":"# before\nspecs = []\nvalidate_genre_specs(specs)\n# after\nspecs = [{\"number\": 1, \"style_fingerprint\": \"noir-neon\", \"dry_run\": True, \"avoid\": [\"generic stock look\"]}]\nvalidate_genre_specs(specs)","handlingStrategy":"validation","validationCode":"specs = config.get(\"genres\", [])\nif not specs:\n    raise SystemExit('config must define at least one genre spec')","typeGuard":"def has_genre_specs(v) -> bool:\n    return isinstance(v, list) and len(v) > 0 and all(isinstance(s, dict) for s in v)","tryCatchPattern":"try:\n    validate_bundle(root)\nexcept ContractError as e:\n    if 'at least one genre spec' in str(e):\n        print('Populate the genre list in your config before validating')\n    else:\n        raise","preventionTips":["Never ship template configs with empty genre lists","Check spec-loading code doesn't filter out all entries","Require the genre argument explicitly rather than defaulting to empty"],"tags":["validation","config","empty"],"backgroundTag":"empty-required-field","analyzedSha":"8321021c54d670126ce3b2969d5deb880b4b0c2a","analyzedAt":"2026-09-16T10:08:13.343Z","contentChangedAt":"2026-09-16T10:08:13.343Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}