{"record":{"id":"515cc836d7b46a78","repo":"affaan-m/ECC","slug":"genre-spec-get-number-has-an-incomplete-signature","errorCode":null,"errorMessage":"genre {spec.get('number')} has an incomplete signature","messagePattern":"genre (.+?) has an incomplete signature","errorType":"validation","errorClass":"ContractError","httpStatus":null,"severity":"error","filePath":"skills/taste-application/scripts/tasteforge/contract.py","lineNumber":166,"sourceCode":"                for group in groups:\n                    if not isinstance(group, dict):\n                        raise ContractError(f\"genre {spec.get('number')} has invalid time evidence\")\n                    for time in group.get(\"times\", []):\n                        _validate_media_time(\n                            time, group.get(\"source_duration\"),\n                            label=f\"genre {spec.get('number')} time evidence\",\n                        )\n            temporal = measured.get(\"temporal\", {})\n            if isinstance(temporal, dict):\n                for group in temporal.get(\"scene_change_evidence\", []):\n                    for time in group.get(\"times\", []):\n                        _validate_media_time(\n                            time, group.get(\"source_duration\"),\n                            label=f\"genre {spec.get('number')} scene evidence\",\n                        )\n        signature = spec.get(\"signature\")\n        if not isinstance(signature, dict) or not _SIGNATURE_AXES.issubset(signature):\n            raise ContractError(f\"genre {spec.get('number')} has an incomplete signature\")\n        if not all(isinstance(signature[axis], list) for axis in _SIGNATURE_AXES):\n            raise ContractError(f\"genre {spec.get('number')} signature axes must be lists\")\n        if not all(signature[axis] for axis in _SIGNATURE_AXES):\n            raise ContractError(f\"genre {spec.get('number')} has an empty signature axis, including avoid\")\n\n\ndef validate_effect_recipe(\n    recipe: dict[str, Any], *, reference_durations: dict[str, float] | None = None\n) -> None:\n    \"\"\"Require a seeded aperiodic schedule and anchors on subject-aware effects.\"\"\"\n    if (recipe.get(\"dry_run\") is not True\n            or type(recipe.get(\"provider_calls\")) is not int\n            or recipe.get(\"provider_calls\") != 0\n            or recipe.get(\"provider_execution\") is not False):\n        raise ContractError(\"effect recipe crosses the dry-run provider boundary\")\n    if not isinstance(recipe.get(\"seed\"), int) or isinstance(recipe.get(\"seed\"), bool):\n        raise ContractError(\"effect recipe must have an integer seed\")\n    if recipe.get(\"rng_algorithm\") != \"python.random.Random/v1\":","sourceCodeStart":148,"sourceCodeEnd":184,"githubUrl":"https://github.com/affaan-m/ECC/blob/8321021c54d670126ce3b2969d5deb880b4b0c2a/skills/taste-application/scripts/tasteforge/contract.py#L148-L184","documentation":"Every genre spec must carry a signature: a dict containing all axes in _SIGNATURE_AXES. This ContractError fires when signature is missing, not a dict, or missing one or more required axis keys — the spec's style signature is incomplete and downstream style-matching cannot run.","triggerScenarios":"Calling validate_genre_specs / validate_bundle with a spec lacking a signature key, signature set to a list/string instead of dict, or signature containing only some axes (e.g. only the positive axes but not the avoid axis).","commonSituations":"Older bundle format from before an axis was added, missing the new key; hand-written specs omitting the avoid axis; signature built conditionally so an empty analysis produced {}.","solutions":["Inspect _SIGNATURE_AXES in contract.py and add every required axis key to the spec's signature dict.","If an axis is unknown, run the signature-extraction step for that spec instead of guessing values.","Upgrade the bundle-generation script to the current schema so all axes are emitted.","Print set(_SIGNATURE_AXES) - set(signature) before validating to see exactly which axes are missing."],"exampleFix":"// before\nspec[\"signature\"] = {\"palette\": [\"warm\"], \"rhythm\": [\"syncopated\"]}  # missing avoid axis\n// after\nspec[\"signature\"] = {\"palette\": [\"warm\"], \"rhythm\": [\"syncopated\"], \"avoid\": [\"harsh-brass\"]}","handlingStrategy":"validation","validationCode":"from skills.taste_application.scripts.tasteforge.contract import _SIGNATURE_AXES\n\ndef signature_is_complete(spec):\n    sig = spec.get(\"signature\")\n    return isinstance(sig, dict) and _SIGNATURE_AXES.issubset(sig)","typeGuard":"def has_complete_signature(spec: dict, axes: set[str]) -> bool:\n    sig = spec.get(\"signature\")\n    return isinstance(sig, dict) and axes.issubset(sig.keys())","tryCatchPattern":"try:\n    validate_genre_specs(specs)\nexcept ContractError as e:\n    if \"incomplete signature\" in str(e):\n        for s in specs:\n            missing = _SIGNATURE_AXES - set(s.get(\"signature\") or {})\n            print(f\"spec {s.get('number')} missing axes: {missing}\")\n    raise","preventionTips":["Import _SIGNATURE_AXES and validate signature completeness right after extraction.","Re-run signature extraction when an axis is missing instead of hand-filling it.","Version bundle schemas so older bundles missing new axes are detected and migrated.","Never build signature dicts conditionally with partial axes."],"tags":["validation","missing-field","signature"],"backgroundTag":"missing-required-config-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"}