affaan-m/ECC · error · ContractError

genre has an incomplete signature

Error message

genre {spec.get('number')} has an incomplete signature

What it means

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.

Solutions

  1. Inspect _SIGNATURE_AXES in contract.py and add every required axis key to the spec's signature dict.
  2. If an axis is unknown, run the signature-extraction step for that spec instead of guessing values.
  3. Upgrade the bundle-generation script to the current schema so all axes are emitted.
  4. Print set(_SIGNATURE_AXES) - set(signature) before validating to see exactly which axes are missing.

Example fix

// before
spec["signature"] = {"palette": ["warm"], "rhythm": ["syncopated"]}  # missing avoid axis
// after
spec["signature"] = {"palette": ["warm"], "rhythm": ["syncopated"], "avoid": ["harsh-brass"]}
Defensive patterns

Strategy: validation

Validate before calling

from skills.taste_application.scripts.tasteforge.contract import _SIGNATURE_AXES

def signature_is_complete(spec):
    sig = spec.get("signature")
    return isinstance(sig, dict) and _SIGNATURE_AXES.issubset(sig)

Type guard

def has_complete_signature(spec: dict, axes: set[str]) -> bool:
    sig = spec.get("signature")
    return isinstance(sig, dict) and axes.issubset(sig.keys())

Try / catch

try:
    validate_genre_specs(specs)
except ContractError as e:
    if "incomplete signature" in str(e):
        for s in specs:
            missing = _SIGNATURE_AXES - set(s.get("signature") or {})
            print(f"spec {s.get('number')} missing axes: {missing}")
    raise

Prevention

When it happens

Trigger: 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).

Common situations: 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 {}.

Understand the failure class

Background: "is required", "must be set", "missing required field": configuration validation errors across open-source libraries — this error's family across 36 libraries.

Related errors


AI-assisted analysis of affaan-m/ECC@8321021c54 (2026-09-16). Data as JSON: /api/errors/515cc836d7b46a78. Report an issue: GitHub.

Appendix: source

Thrown at skills/taste-application/scripts/tasteforge/contract.py:166

                for group in groups:
                    if not isinstance(group, dict):
                        raise ContractError(f"genre {spec.get('number')} has invalid time evidence")
                    for time in group.get("times", []):
                        _validate_media_time(
                            time, group.get("source_duration"),
                            label=f"genre {spec.get('number')} time evidence",
                        )
            temporal = measured.get("temporal", {})
            if isinstance(temporal, dict):
                for group in temporal.get("scene_change_evidence", []):
                    for time in group.get("times", []):
                        _validate_media_time(
                            time, group.get("source_duration"),
                            label=f"genre {spec.get('number')} scene evidence",
                        )
        signature = spec.get("signature")
        if not isinstance(signature, dict) or not _SIGNATURE_AXES.issubset(signature):
            raise ContractError(f"genre {spec.get('number')} has an incomplete signature")
        if not all(isinstance(signature[axis], list) for axis in _SIGNATURE_AXES):
            raise ContractError(f"genre {spec.get('number')} signature axes must be lists")
        if not all(signature[axis] for axis in _SIGNATURE_AXES):
            raise ContractError(f"genre {spec.get('number')} has an empty signature axis, including avoid")


def validate_effect_recipe(
    recipe: dict[str, Any], *, reference_durations: dict[str, float] | None = None
) -> None:
    """Require a seeded aperiodic schedule and anchors on subject-aware effects."""
    if (recipe.get("dry_run") is not True
            or type(recipe.get("provider_calls")) is not int
            or recipe.get("provider_calls") != 0
            or recipe.get("provider_execution") is not False):
        raise ContractError("effect recipe crosses the dry-run provider boundary")
    if not isinstance(recipe.get("seed"), int) or isinstance(recipe.get("seed"), bool):
        raise ContractError("effect recipe must have an integer seed")
    if recipe.get("rng_algorithm") != "python.random.Random/v1":

View on GitHub (pinned to 8321021c54)