affaan-m/ECC · error · ContractError
genre crosses the dry-run boundary
Error message
genre {spec.get('number')} crosses the dry-run boundary What it means
Every genre spec must set dry_run to exactly True (identity check with `is not True`, not just truthy). This is a dry-run boundary: the validator refuses specs that have not explicitly opted into non-destructive measurement mode, so accidental live generation can never pass contract validation. Strings like "true" or 1 do not satisfy the check.
Solutions
- Set dry_run: true (a real boolean) on every genre spec in the bundle.
- Coerce loaded config values explicitly: spec["dry_run"] = spec.get("dry_run") is True or bool(...) with strict parsing.
- Check your config parser/loader for values arriving as strings or ints and normalize them at load time.
- Search the bundle for any specs missing the dry_run key and add it.
Example fix
// before
{"number": 3, "dry_run": "true", ...}
// after
{"number": 3, "dry_run": true, ...} Defensive patterns
Strategy: validation
Validate before calling
def ensure_dry_run(specs):
for s in specs:
if s.get("dry_run") is not True:
raise ValueError(f"spec {s.get('number')} must set dry_run: true (boolean)") Type guard
def is_explicit_dry_run(spec: dict) -> bool:
return spec.get("dry_run") is True Try / catch
try:
validate_genre_specs(specs)
except ContractError as e:
if "crosses the dry-run boundary" in str(e):
for s in specs:
s["dry_run"] = (s.get("dry_run") is True)
raise Prevention
- Always write dry_run: true as a boolean in configs, never "true" or 1.
- Normalize boolean config fields at load time with strict bool parsing.
- Include dry_run in the spec template so it is never omitted.
- Reject non-bool dry_run values at config-load time.
When it happens
Trigger: Passing a spec with dry_run missing, set to False, set to 1, or set to the string "true" into validate_genre_specs (directly or through validate_bundle).
Common situations: Hand-authored YAML/JSON configs where dry_run is written as "true" (a string); deserialized configs where JSON has no native bool and the value arrives as 1/0; templates that omit the field entirely; older bundle formats predating the dry-run requirement.
Understand the failure class
Background: "unknown output mode", "invalid value for flag", "expects true/false": fixing invalid flag value errors in CLI tools — this error's family across 24 libraries.
Related errors
- ECC_DRY_RUN must be "1" or "0" when set
- effect recipe crosses the dry-run provider boundary
- missing modality manifests
- workflow requires dry_run=true; provider execution is…
- -32602
AI-assisted analysis of affaan-m/ECC@8321021c54 (2026-09-16).
Data as JSON: /api/errors/a6e40ec773a25e0d.
Report an issue: GitHub.
Appendix: source
Thrown at skills/taste-application/scripts/tasteforge/contract.py:132
raise ContractError(f"output bundle contains a special file: {entry.path}")
def validate_genre_specs(specs: list[dict[str, Any]]) -> None:
"""Require complete, semantically distinct numbered genre specs."""
if not specs:
raise ContractError("at least one genre spec is required")
numbers = [spec.get("number") for spec in specs]
if len(numbers) != len(set(numbers)):
raise ContractError("genre numbers must be distinct")
fingerprints = [spec.get("style_fingerprint") for spec in specs]
signatures = [_semantic_signature(spec) for spec in specs]
if len(fingerprints) != len(set(fingerprints)) or len(signatures) != len(set(signatures)):
raise ContractError("genre references collapsed into a generic style; distinct specs required")
for spec in specs:
if spec.get("dry_run") is not True:
raise ContractError(f"genre {spec.get('number')} crosses the dry-run boundary")
measured = spec.get("measured_features")
if measured is not None:
if not isinstance(measured, dict):
raise ContractError(f"genre {spec.get('number')} has invalid measured evidence")
_validate_numeric_evidence(measured, label=f"genre {spec.get('number')} evidence")
total_duration = measured.get("total_duration")
if not _is_finite_real(total_duration):
raise ContractError(f"genre {spec.get('number')} has invalid total duration")
total_duration = cast(float, total_duration)
if float(total_duration) <= 0:
raise ContractError(f"genre {spec.get('number')} has invalid total duration")
for group_name in ("sample_times",):
groups = measured.get(group_name, [])
if not isinstance(groups, list):
raise ContractError(f"genre {spec.get('number')} has invalid time evidence")
for group in groups:
if not isinstance(group, dict):
raise ContractError(f"genre {spec.get('number')} has invalid time evidence")View on GitHub (pinned to 8321021c54)