Building in the open

Find the first place a CTV ad transaction stops agreeing with itself

A streaming ad break passes through an ad server, a stitcher, a manifest, a player and a stack of beacons, and each keeps its own record. OpenCTV lines them up as one transaction and finds the earliest point where they diverge.

How it works

One ad break, seven records, one first disagreement

Discrepancies usually get argued at the end of the chain, as two totals that do not match. OpenCTV walks the chain in order instead, and stops at the first step where the evidence no longer agrees with the step before it.

  1. 1DecisionOpenRTB
  2. 2Ad responseVAST
  3. 3InsertionSSAI, SGAI
  4. 4 ยท first divergenceManifestHLS
  5. 5PlaybackPlayer
  6. 6MeasurementBeacons
  7. 7BillingReconciliation
sketch, not real output
ad response   creative     30.0s   VAST
insertion     break filled 30.0s   stitcher log
manifest      break window 15.0s   HLS DATERANGE
first divergence  insertion โ†’ manifest

In the sketch, the stitcher filled a 30 second break but the manifest only opened 15 seconds of it. Everything downstream is internally consistent and still wrong: the player shows half the ad, the later quartile beacons never fire, and the invoice and the measurement report will not reconcile.

The decision stage is classified but not executed. Bid-side signals cannot be observed end to end by any one party, so OpenCTV names those failures without pretending to replay them. HLS comes first; DASH is out of scope for the first release.

What it reports

Four kinds of output, kept apart on purpose

Most tools blur what they saw with what they concluded. OpenCTV keeps them separate, and when the evidence runs out it says so instead of guessing.

evidence

What a system emitted

A record of what one system actually produced, with where it came from and when.

change

What moved between looks

What differs between two observations of the same thing, such as two refreshes of one manifest.

finding

A named divergence

A divergence matched to a library entry, carrying the chain of evidence that supports it.

unresolved

What the evidence cannot settle

Where the trail stops, what is missing, and what would settle it. Insufficient evidence is an answer.

Components

Five pieces, each useful on its own

They ship in this order, and the last one ties the first four together. Each release is announced here and on GitHub when it is ready, not on a date.

In progress

divergence-library

A taxonomy of how CTV ad transactions go wrong, and the shared schemas the other four depend on. Every entry starts from a specification read or a failure observed. None are generated.

Next

hls-manifest-diff

Compares two HLS manifests by meaning rather than by text, and shows what actually changed. Built to be useful to streaming engineers who never touch advertising.

Planned

stream-fixtures

A reproducible corpus of broken streams, each tied to a library entry and checked byte for byte, so a defect can be handed over as something to run rather than something to describe.

Planned

cuechain

Follows one ad break signal from SCTE-35 through every cue that claims to represent it, and reports where they stop telling the same story.

Planned

openctv

One command, one report, one evidence chain. Runs on your machine with a local UI and exports a single HTML file you can attach to a ticket.

Code under Apache-2.0. The library, schemas and fixtures under CC-BY-4.0.

Principles

How the work gets done

OpenCTV replaces no standard and no vendor tool. It is the layer between them: the part nobody owns, and the part where most of the failures actually live.

Depend, never reimplement

Existing open parsers and test servers are used as they are. Published specifications decide every rule.

Evidence or nothing

Every finding cites what it rests on. A gap in the evidence is reported as a gap, never filled with a plausible story.

Releases, not dates

Something ships when it installs clean, its quickstart works and its tests pass. The roadmap carries no dates.

Local first

It runs against your own captures on your own machine. There is no hosted service to send traffic to.