Configuring Make-Good Triggers Based on Ratings

Post-air telemetry from Nielsen or Comscore frequently exposes soft under-delivery against contracted GRP or impression guarantees. When delivery ratios fall below contractual floors, broadcast traffic operations must execute make-good routing before cure windows expire. Manual reconciliation introduces latency, inflates billing disputes, and fragments forward inventory forecasting. The operational objective is to deploy a deterministic, ratings-driven trigger that evaluates delivery ratios, enforces compliance boundaries, resolves slot availability, and emits structured make-good orders to the traffic system without human intervention. This workflow operates as a downstream extension of the Spot Scheduling Validation & Rule Engines architecture, shifting from static schedule validation to dynamic, data-reactive fulfillment.

Unlike hard preemption scenarios where technical failures or breaking news force immediate spot displacement, ratings-based triggers address statistical under-delivery over multi-day windows. While Automating Make-Good Routing for Preemptions focuses on instantaneous inventory reallocation following confirmed air breaks, ratings triggers require asynchronous evaluation, configurable tolerance modeling, and multi-day inventory balancing. The following implementation details a production-ready Python automation service that ingests post-air ratings payloads, applies configurable delivery thresholds, validates contractual compliance, detects scheduling conflicts, and routes make-good orders with explicit error boundaries.

Architectural Context & Data Flow

The trigger pipeline operates as an event-driven microservice that subscribes to post-air telemetry streams. Each payload undergoes schema validation, temporal normalization, and idempotency hashing before entering the evaluation engine. The core state machine transitions through four deterministic phases:

  1. Ingestion & Normalization: Raw telemetry is parsed, timestamps are aligned to RFC 3339 standards, and missing fields trigger immediate rejection to a dead-letter queue.
  2. Threshold Evaluation: Actual GRPs/impressions are compared against contracted baselines using configurable tolerance bands. Statistical variance is accounted for to prevent false triggers on marginal delivery gaps.
  3. Conflict Resolution: The engine queries available inventory windows, applies priority weighting, and validates against blackout rules, competitive separation, and programmatic pacing constraints.
  4. Order Emission: A structured make-good payload is serialized, cryptographically signed, and pushed to the traffic management system via authenticated REST or AMQP endpoints.
flowchart TD
    A["Compute delivery ratio<br/>actual / expected"] --> B{"ratio >= tolerance?"}
    B -->|"yes"| C["On-target (OK)"]
    B -->|"no"| D{"ratio < 0.5?"}
    D -->|"yes"| E["Compliance violation"]
    D -->|"no"| F["Trigger make-good"]
    F --> G["Resolve inventory"]

Figure — Ratings trigger: the delivery ratio is compared to tolerance, flagged as a compliance violation below 0.5, otherwise it triggers a make-good and inventory resolution.

Production Implementation

The following module demonstrates a production-grade implementation adhering to strict typing, structured audit logging, and explicit error boundaries. It is designed for integration into containerized broadcast automation stacks.

python
import logging
import uuid
import hashlib
from dataclasses import dataclass, field
from datetime import datetime, timezone
from typing import Optional, List, Dict, Any, Tuple
from enum import Enum

# Configure structured JSON logging for audit compliance
logging.basicConfig(level=logging.INFO, format="%(message)s")
logger = logging.getLogger(__name__)

class TriggerStatus(Enum):
    TRIGGERED = "triggered"
    WITHIN_TOLERANCE = "within_tolerance"
    INSUFFICIENT_DATA = "insufficient_data"
    COMPLIANCE_VIOLATION = "compliance_violation"
    ROUTING_FAILED = "routing_failed"

@dataclass(frozen=True)
class RatingsPayload:
    spot_id: str
    market: str
    aired_start: datetime
    aired_end: datetime
    contracted_grps: float
    actual_grps: Optional[float]
    threshold_pct: float = 0.95
    make_good_priority: int = 1
    client_id: str = ""
    trace_id: str = field(default_factory=lambda: str(uuid.uuid4()))

class RatingsEvaluationError(Exception):
    """Raised when ratings data fails validation or compliance checks."""
    pass

class ConflictResolutionError(Exception):
    """Raised when no viable inventory windows exist for make-good routing."""
    pass

class RatingsMakeGoodEngine:
    """
    Evaluates post-air ratings against contractual thresholds and triggers 
    make-good routing when under-delivery exceeds configured tolerances.
    """
    def __init__(self, compliance_rules: Dict[str, Any], conflict_service: Any):
        self.compliance_rules = compliance_rules
        self.conflict_service = conflict_service
        self._audit_buffer: List[Dict[str, Any]] = []

    def _generate_idempotency_key(self, payload: RatingsPayload) -> str:
        raw = f"{payload.spot_id}:{payload.market}:{payload.aired_start.isoformat()}"
        return hashlib.sha256(raw.encode()).hexdigest()

    def evaluate_delivery(self, payload: RatingsPayload) -> Tuple[TriggerStatus, float]:
        if payload.actual_grps is None:
            return TriggerStatus.INSUFFICIENT_DATA, 0.0
        
        if payload.contracted_grps <= 0:
            raise RatingsEvaluationError("Contracted GRPs must be positive")

        delivery_ratio = payload.actual_grps / payload.contracted_grps
        tolerance = payload.threshold_pct
        
        if delivery_ratio >= tolerance:
            return TriggerStatus.WITHIN_TOLERANCE, delivery_ratio
        elif delivery_ratio < 0.5:
            return TriggerStatus.COMPLIANCE_VIOLATION, delivery_ratio
        
        return TriggerStatus.TRIGGERED, delivery_ratio

    def resolve_inventory(self, payload: RatingsPayload, deficit_grps: float) -> Dict[str, Any]:
        """
        Queries downstream conflict service for viable replacement windows.
        Implements retry-aware inventory balancing.
        """
        try:
            windows = self.conflict_service.find_available_slots(
                market=payload.market,
                min_duration_minutes=30,
                priority=payload.make_good_priority,
                required_grps=deficit_grps
            )
            if not windows:
                raise ConflictResolutionError("No compliant inventory windows available")
            return windows[0]
        except Exception as exc:
            logger.error("Inventory resolution failed: %s", exc, extra={"trace_id": payload.trace_id})
            raise

    def emit_make_good_order(self, payload: RatingsPayload, window: Dict[str, Any]) -> Dict[str, Any]:
        order = {
            "order_id": f"MG-{uuid.uuid4().hex[:8].upper()}",
            "source_spot_id": payload.spot_id,
            "client_id": payload.client_id,
            "market": payload.market,
            "scheduled_start": window["start"],
            "scheduled_end": window["end"],
            "program_id": window["program_id"],
            "priority": payload.make_good_priority,
            "idempotency_key": self._generate_idempotency_key(payload),
            "audit_trace": payload.trace_id,
            "emitted_at": datetime.now(timezone.utc).isoformat()
        }
        logger.info("Make-good order emitted", extra={"payload": order})
        return order

    def execute(self, payload: RatingsPayload) -> Dict[str, Any]:
        status, ratio = self.evaluate_delivery(payload)
        audit_entry = {
            "trace_id": payload.trace_id,
            "spot_id": payload.spot_id,
            "status": status.value,
            "delivery_ratio": ratio,
            "timestamp": datetime.now(timezone.utc).isoformat()
        }
        self._audit_buffer.append(audit_entry)

        if status == TriggerStatus.WITHIN_TOLERANCE:
            return {"status": status.value, "action": "none", "audit": audit_entry}
        if status == TriggerStatus.INSUFFICIENT_DATA:
            return {"status": status.value, "action": "defer", "audit": audit_entry}
        if status == TriggerStatus.COMPLIANCE_VIOLATION:
            raise RatingsEvaluationError(f"Critical under-delivery detected: {ratio:.2%}")

        deficit = payload.contracted_grps - payload.actual_grps
        try:
            window = self.resolve_inventory(payload, deficit)
            order = self.emit_make_good_order(payload, window)
            audit_entry.update({"action": "routed", "order_id": order["order_id"]})
            return {"status": TriggerStatus.TRIGGERED.value, "action": "routed", "order": order, "audit": audit_entry}
        except ConflictResolutionError:
            audit_entry.update({"action": "failed", "reason": "inventory_deadlock"})
            return {"status": TriggerStatus.ROUTING_FAILED.value, "action": "failed", "audit": audit_entry}

Threshold Configuration & Tolerance Modeling

Ratings volatility requires statistical tolerance rather than absolute thresholds. Hard cutoffs at 100% delivery generate excessive make-good volume due to normal measurement variance. Implement sliding tolerance bands that scale with market size and demographic granularity:

  • National/Regional: 95% threshold with ±2% measurement error buffer
  • DMA/Local: 92% threshold with ±4% buffer
  • Niche Demographics: 90% threshold with rolling 7-day averaging

Tolerance configuration should be externalized to a version-controlled YAML or database-backed policy store. The engine must reload thresholds without restarting, applying them atomically to prevent mid-evaluation drift. Contractual SLAs often specify cure windows (e.g., 14–30 days). The trigger must calculate remaining cure time and escalate priority as deadlines approach.

Audit Logging & Compliance Tracing

Broadcast operations require immutable audit trails for billing reconciliation and regulatory compliance. All evaluation steps must emit structured logs containing trace identifiers, payload hashes, and state transitions. Python’s built-in logging framework supports JSON serialization via custom formatters, enabling seamless ingestion into Elasticsearch or Splunk.

Implement idempotency keys at ingestion to prevent duplicate make-good generation during telemetry retries. Each emitted order must include the original trace ID, enabling end-to-end correlation across the ratings pipeline, inventory resolver, and traffic scheduler. For long-running evaluations, implement circuit breakers around external conflict resolution APIs to prevent cascade failures during peak reconciliation windows.

Operational Recovery & Troubleshooting

Production environments encounter stale payloads, API timeouts, and inventory deadlocks. The following recovery patterns ensure deterministic behavior:

  1. Stale Telemetry Handling: Reject payloads where aired_end exceeds 48 hours. Route to a quarantine queue for manual review rather than triggering retroactive make-goods that violate pacing contracts.
  2. Inventory Deadlocks: When the conflict service returns zero viable windows, implement exponential backoff with jitter. After three failed attempts, escalate to a human-in-the-loop dashboard with pre-calculated deficit metrics and alternative program recommendations.
  3. Idempotency Enforcement: The traffic system must reject duplicate order_id or idempotency_key submissions. Implement a Redis-backed key store with TTL matching the contractual cure window to prevent replay attacks.
  4. State Rollback: If a make-good order fails post-emission due to downstream validation, the engine must emit a compensating cancellation event and revert the inventory reservation. Never leave partial state in the scheduler.
  5. Metrics & Alerting: Expose Prometheus metrics for ratings_evaluation_duration_seconds, make_good_trigger_rate_total, and inventory_resolution_failures_total. Alert on sustained routing failure rates exceeding 5% over 15-minute windows.

Deployment should leverage infrastructure-as-code templates with environment-specific threshold overrides. Secrets for telemetry API keys and traffic system credentials must be injected via vault integrations, never hardcoded. Schedule the evaluation service via Kubernetes CronJobs or Apache Airflow DAGs aligned with Nielsen/Comscore data drop SLAs, typically T+1 or T+2 post-air.

Configuring make-good triggers based on ratings transforms reactive billing disputes into proactive inventory optimization. By enforcing strict evaluation boundaries, maintaining immutable audit trails, and implementing deterministic recovery patterns, broadcast operations can guarantee contractual fulfillment while preserving forward inventory integrity.