wai: a channel's breaks marked in its HLS Media Playlist, and why a delivery took the route it took #636

Merged
dcharlot merged 1 commit from wai/hls-cues-delivery-decision into main 2026-11-05 07:52:53 -05:00
Owner

What this is

Two additions to existing extensions:

  • channel-lineup §7.4 defines the cue tags a packager writes in a channel's HLS Media Playlist, from the channel's signed schedule.
  • route-record §6.3 defines a record of why a delivery of a job's output took the route it took.

Merge order. This branch sits on #632's commit, 946f3beda, which moves changelog entries into wai/changes/ and the conformance workflow's lists into step files. Merge #632 first; until it merges, this pull request's diff also shows #632's change.

What it carries. One commit, 5bdb727e9: the change of wai/hls-cues-delivery-decision (98192fe63..c42fa8c9c) in #632's layout. Its message lists the source commits. The branch's changelog entry is a file of wai/changes/. Its registry row stays where the source branch put it, beside route-record §6.2. The existing channel-lineup and route-record steps already run both corpora, so it adds no step file. Every other file is as the source branch has it.

1. The cue tags (channel-lineup §7.4, §8.4)

What it defines. A packager that writes a channel's segments marks each break of the wai.video.channel receipt's schedule, in two forms:

  • EXT-X-CUE-OUT with the break's duration on its first segment, EXT-X-CUE-OUT-CONT with the time elapsed and the duration on each later one, and EXT-X-CUE-IN on its return;
  • for a break whose cue carries its SCTE-35 section, the EXT-X-DATERANGE pair of RFC 8216 §4.3.2.7.1 as well. SCTE35-OUT and PLANNED-DURATION go with the last segment before the break, kept while the window holds a segment of it, then DURATION goes on its return. A date range's ID is the break's id and its start, so a break id a schedule repeats on another day names another range.

A segment carries the same cue lines in every window that holds it. A break whose start or end falls inside a segment is refused cue_unaligned. A break whose id holds a double quote, a line feed or a carriage return, which no quoted-string can hold, is refused cue_unwritable. A window's last segment ends before the year 10000, so every date the playlist writes has four digits of year.

Its verification (§8.4) takes a receipt, the cues its import wrote (cues.json) and a playlist. Each cue is held to its break's id, start and duration, and through its cue id to a splice_insert()'s splice_event_id (cues_malformed, cues_mismatch). The playlist's window is read back (playlist_malformed), and its tags are rendered again and compared byte for byte (playlist_mismatch). The receipt signs each break's id, start and duration, which every tag is written from. It does not sign the section a date range carries, and §12 says so.

The reference. channel_lineup::cue_tags (render, read_playlist, verify_playlist), and channel_lineup::import::break_cue, the cue §4.8 yields for a break.

The corpus. live-conformance/channels/ gains the sample's cues and the receipts or cues of further channels: the JSON form with a section left out, the three characters no quoted-string holds, and a channel scheduled at the end of the year 9999. It gains playlist windows and playlist verifications. Among the windows are one that begins inside a break, one at a return, one with no section, and segments of 6.006 seconds. Others sit at each bound of §7.4. The refused windows are a control of each refusal of §7.4 and one past each bound. Each verification that is not accepted is a control of one step.

The independent verifier. channel_lineup_verify.py is written from the extension's text and shares no code with wai-rs. It renders and reads the playlists itself. It catches each of its new checks when that check is dropped, among them one for each bound and one for each character no quoted-string holds, and it requires every new mutated copy of the corpus to fail.

Breaking: six new channel_lineup::Reason variants, which an exhaustive match over Reason needs.

2. The delivery-decision record (route-record §6.3, §7.2 step 10)

What it defines. wai-route-delivery-decision records why a delivery of a job's output took the route it took. A delivery is routed as a job is, from its own request, price book of version 2 and policy. Its candidates are the routes that could carry it (an origin, a cache, a bearer), with every limit that removed one. The record:

  • restates every route as its price book stated it, with its estimates and expected figures;
  • states the order, the decision and the route taken;
  • states in passed the key by which each other surviving route was passed over;
  • names its inputs by digest: the delivery's three documents, and as its parent, the job's route record, whose served hop's output it carries by count and root.

The job's router signs it under its own domain. Decisions are chained: each one after a job's first names the one before it by prev, so a decision left out before a later one, moved, or out of order is refused. The list proves nothing of decisions after its last, since a verifier is not told how many there were.

Its verification. A verification document may have deliveries: each one is a delivery's three documents and its decision, and they hold the job's decisions from the first. The checks run in this order:

  • each decision is read and held to its documents (request_mismatch, policy_mismatch, pricebook_mismatch);
  • its routing is recomputed (candidate_mismatch, removal_mismatch, rank_mismatch, decision_mismatch);
  • its route and its passed routes are checked (delivery_route_mismatch);
  • then its parent (delivery_parent_mismatch);
  • last, its chain (delivery_chain_broken).

A form that breaks §6.3 is delivery_invalid. No route record changes, and a document without deliveries reads exactly as before.

The reference. route_record::delivery (seal, passed, delivered_by, form). The route record's candidate form and step 6 are shared with it, unchanged.

The corpus. live-conformance/routes/ gains accepted deliveries and controls, each a control of one defect, refused at its own place. Each member that binds a decision to its documents has a control of its own. The accepted ones are:

  • a delivery that one cache wins on time to first byte over another of one price;
  • two decisions chained;
  • a delivery no route survives;
  • one under a record of version 2;
  • one verified without receipts;
  • an empty deliveries.

The independent verifier. route_record_verify.py is written from the extension's text and shares no code with wai-rs. It verifies each vector from the text, catches each of its new checks when that check is dropped, and requires every mutated copy of the corpus to fail.

The registry lists wai-route-delivery-decision as a document kind of route-record §6.3.

Breaking: route_record::Accepted::deliveries and route_record::Document::deliveries, which a struct literal needs; four new route_record::Reason variants; and Reason::ALL, which holds them.

How it was verified

This commit, 5bdb727e9.

  • Locally (git and Python checks only): scripts/wai-conformance-lists.py selftest and check, scripts/wai-conformance-lists-selftest.sh, scripts/check-changelog-fragment.py against main, scripts/changelog-assemble.py --check and scripts/changelog-fragments-selftest.sh all pass. Rebasing from the conversion's first base, #632's earlier tip 14f1abb7c, onto 946f3beda left every file's change the same.
  • On a Linux x86_64 build host: in wai/wai-rs, cargo check --locked --no-default-features --tests with channel_lineup, with route_record, and with both together. All pass.
  • With the other converted branches. This branch merges without conflict with each of the other branches converted to #632's layout (by git merge-tree). Merging all of them in forward, reverse and a mixed order gives one tree. On the build host, that tree passes these runs: cargo check --tests under the derived WAI_FEATURES and under WAI_PQ_FEATURES; cargo check --tests of quickstart,app; the catalogue's library tests; and the wai binary's unit tests.

The modules' tests, the corpora's regeneration and the two Python verifiers did not run on this commit here; their results come from this pull request's CI.

On the CI hosts. Results on the CI runners come from this pull request's own CI run. None of the runs above is a CI result.

🤖 Generated with Claude Code

## What this is Two additions to existing extensions: - channel-lineup §7.4 defines the cue tags a packager writes in a channel's HLS Media Playlist, from the channel's signed schedule. - route-record §6.3 defines a record of why a delivery of a job's output took the route it took. **Merge order.** This branch sits on #632's commit, 946f3beda, which moves changelog entries into `wai/changes/` and the conformance workflow's lists into step files. Merge #632 first; until it merges, this pull request's diff also shows #632's change. **What it carries.** One commit, 5bdb727e9: the change of `wai/hls-cues-delivery-decision` (98192fe63..c42fa8c9c) in #632's layout. Its message lists the source commits. The branch's changelog entry is a file of `wai/changes/`. Its registry row stays where the source branch put it, beside route-record §6.2. The existing channel-lineup and route-record steps already run both corpora, so it adds no step file. Every other file is as the source branch has it. ## 1. The cue tags (channel-lineup §7.4, §8.4) **What it defines.** A packager that writes a channel's segments marks each break of the `wai.video.channel` receipt's schedule, in two forms: - `EXT-X-CUE-OUT` with the break's duration on its first segment, `EXT-X-CUE-OUT-CONT` with the time elapsed and the duration on each later one, and `EXT-X-CUE-IN` on its return; - for a break whose cue carries its SCTE-35 section, the `EXT-X-DATERANGE` pair of RFC 8216 §4.3.2.7.1 as well. `SCTE35-OUT` and `PLANNED-DURATION` go with the last segment before the break, kept while the window holds a segment of it, then `DURATION` goes on its return. A date range's `ID` is the break's id and its start, so a break id a schedule repeats on another day names another range. A segment carries the same cue lines in every window that holds it. A break whose start or end falls inside a segment is refused `cue_unaligned`. A break whose id holds a double quote, a line feed or a carriage return, which no quoted-string can hold, is refused `cue_unwritable`. A window's last segment ends before the year 10000, so every date the playlist writes has four digits of year. **Its verification** (§8.4) takes a receipt, the cues its import wrote (`cues.json`) and a playlist. Each cue is held to its break's id, start and duration, and through its cue id to a `splice_insert()`'s `splice_event_id` (`cues_malformed`, `cues_mismatch`). The playlist's window is read back (`playlist_malformed`), and its tags are rendered again and compared byte for byte (`playlist_mismatch`). The receipt signs each break's id, start and duration, which every tag is written from. It does not sign the section a date range carries, and §12 says so. **The reference.** `channel_lineup::cue_tags` (`render`, `read_playlist`, `verify_playlist`), and `channel_lineup::import::break_cue`, the cue §4.8 yields for a break. **The corpus.** `live-conformance/channels/` gains the sample's cues and the receipts or cues of further channels: the JSON form with a section left out, the three characters no quoted-string holds, and a channel scheduled at the end of the year 9999. It gains playlist windows and playlist verifications. Among the windows are one that begins inside a break, one at a return, one with no section, and segments of 6.006 seconds. Others sit at each bound of §7.4. The refused windows are a control of each refusal of §7.4 and one past each bound. Each verification that is not accepted is a control of one step. **The independent verifier.** `channel_lineup_verify.py` is written from the extension's text and shares no code with `wai-rs`. It renders and reads the playlists itself. It catches each of its new checks when that check is dropped, among them one for each bound and one for each character no quoted-string holds, and it requires every new mutated copy of the corpus to fail. **Breaking:** six new `channel_lineup::Reason` variants, which an exhaustive match over `Reason` needs. ## 2. The delivery-decision record (route-record §6.3, §7.2 step 10) **What it defines.** `wai-route-delivery-decision` records why a delivery of a job's output took the route it took. A delivery is routed as a job is, from its own request, price book of version 2 and policy. Its candidates are the routes that could carry it (an origin, a cache, a bearer), with every limit that removed one. The record: - restates every route as its price book stated it, with its estimates and expected figures; - states the order, the decision and the route taken; - states in `passed` the key by which each other surviving route was passed over; - names its inputs by digest: the delivery's three documents, and as its `parent`, the job's route record, whose served hop's output it carries by count and root. The job's router signs it under its own domain. Decisions are chained: each one after a job's first names the one before it by `prev`, so a decision left out before a later one, moved, or out of order is refused. The list proves nothing of decisions after its last, since a verifier is not told how many there were. **Its verification.** A verification document may have `deliveries`: each one is a delivery's three documents and its decision, and they hold the job's decisions from the first. The checks run in this order: - each decision is read and held to its documents (`request_mismatch`, `policy_mismatch`, `pricebook_mismatch`); - its routing is recomputed (`candidate_mismatch`, `removal_mismatch`, `rank_mismatch`, `decision_mismatch`); - its route and its passed routes are checked (`delivery_route_mismatch`); - then its parent (`delivery_parent_mismatch`); - last, its chain (`delivery_chain_broken`). A form that breaks §6.3 is `delivery_invalid`. No route record changes, and a document without `deliveries` reads exactly as before. **The reference.** `route_record::delivery` (`seal`, `passed`, `delivered_by`, `form`). The route record's candidate form and step 6 are shared with it, unchanged. **The corpus.** `live-conformance/routes/` gains accepted deliveries and controls, each a control of one defect, refused at its own place. Each member that binds a decision to its documents has a control of its own. The accepted ones are: - a delivery that one cache wins on time to first byte over another of one price; - two decisions chained; - a delivery no route survives; - one under a record of version 2; - one verified without receipts; - an empty `deliveries`. **The independent verifier.** `route_record_verify.py` is written from the extension's text and shares no code with `wai-rs`. It verifies each vector from the text, catches each of its new checks when that check is dropped, and requires every mutated copy of the corpus to fail. **The registry** lists `wai-route-delivery-decision` as a document kind of route-record §6.3. **Breaking:** `route_record::Accepted::deliveries` and `route_record::Document::deliveries`, which a struct literal needs; four new `route_record::Reason` variants; and `Reason::ALL`, which holds them. ## How it was verified **This commit, 5bdb727e9.** - **Locally** (git and Python checks only): `scripts/wai-conformance-lists.py selftest` and `check`, `scripts/wai-conformance-lists-selftest.sh`, `scripts/check-changelog-fragment.py` against main, `scripts/changelog-assemble.py --check` and `scripts/changelog-fragments-selftest.sh` all pass. Rebasing from the conversion's first base, #632's earlier tip 14f1abb7c, onto 946f3beda left every file's change the same. - **On a Linux x86_64 build host:** in `wai/wai-rs`, `cargo check --locked --no-default-features --tests` with `channel_lineup`, with `route_record`, and with both together. All pass. - **With the other converted branches.** This branch merges without conflict with each of the other branches converted to #632's layout (by `git merge-tree`). Merging all of them in forward, reverse and a mixed order gives one tree. On the build host, that tree passes these runs: `cargo check --tests` under the derived `WAI_FEATURES` and under `WAI_PQ_FEATURES`; `cargo check --tests` of `quickstart,app`; the catalogue's library tests; and the `wai` binary's unit tests. The modules' tests, the corpora's regeneration and the two Python verifiers did not run on this commit here; their results come from this pull request's CI. **On the CI hosts.** Results on the CI runners come from this pull request's own CI run. None of the runs above is a CI result. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
wai/hls-cues-delivery-decision, converted to the layout of the fix: no shared append point
All checks were successful
standards workspaces build + test / joule-code-rs (joule-code/joule-code-rs, joule-code-rs, linux-arm64, true) (pull_request) Successful in 6m29s
wai-audit collector and player integration / collector, player integration and the browser end to end · macos (pull_request) Successful in 1m25s
wai-app and Create / wai-app · Create, browser end-to-end (macos) (pull_request) Successful in 7m16s
WAI conformance / cross-arch byte-equality · macos (pull_request) Successful in 27s
WAI conformance / codec wrappers · fuzz smoke, text decode limits, C entry points, provenance, the semantic path (pull_request) Successful in 1m9s
joule-moq-web browser client / joule-moq-web · browser end-to-end (macos) (pull_request) Successful in 12m29s
standards workspaces build + test / macOS energy backends (pull_request) Successful in 10m34s
wai-ledger service / service · macos (pull_request) Successful in 48s
WAI conformance / cargo-class corpora · linux-arm64 (pull_request) Successful in 16m47s
wai-fleet service / service · macos (pull_request) Successful in 1m3s
WAI conformance / delivery telemetry · CMCD and CMSD against an independent library (pull_request) Successful in 29s
WAI conformance / each feature of the independent verifiers' steps compiles alone (pull_request) Successful in 11m30s
WAI conformance / cross-arch byte-equality · linux-arm64 (pull_request) Successful in 34s
wai-pack / wai-pack · macos (pull_request) Successful in 2m30s
WAI energy receipts / energy receipts · macos (pull_request) Successful in 7m25s
WAI conformance / C2PA manifest emission · signed, read back, validated (pull_request) Successful in 1m52s
WAI conformance / cargo-class corpora · macos (pull_request) Successful in 10m38s
wai-quantum studio and desktop / desktop app · macos (pull_request) Successful in 4m50s
wai-sink-android native sink library / every ABI, the AAR, and the instrumented tests in a headless emulator · macos (pull_request) Successful in 4m58s
WAI examples / WAI examples · android-app · linux-arm64 (pull_request) Successful in 5m54s
wai-pack / wai-pack · linux-arm64 (pull_request) Successful in 1m44s
wai-pack / wai-pack · the floor through a media prober (pull_request) Successful in 25s
wai-pack / wai-pack · dependency licences (pull_request) Successful in 17s
WAI examples / WAI examples · the archive, the readiness check, the browser and Apple · macos (pull_request) Successful in 15m5s
WAI Studio / Studio in Chromium and Firefox · macos (pull_request) Successful in 7m37s
WAI energy receipts / energy receipts · linux-arm64 (pull_request) Successful in 11m59s
wai-sink-android native sink library / the JNI layer and the platform-free half on the host JVM · linux-arm64 (pull_request) Successful in 2m23s
wai-sink-swift native sink package / libwai slices, desktop tests, simulator tests · macos (pull_request) Successful in 15m57s
wai-pack / wai-pack · independent verifier (Python) (pull_request) Successful in 9m16s
WAI conformance / independent verifiers · claims, JWP, key discovery and integer payloads (Python) (pull_request) Successful in 1h13m0s
5bdb727e98
The change of wai/hls-cues-delivery-decision (98192fe63..c42fa8c9c, 4 commits), on the fix branch: its
changelog entries as files of wai/changes/, its wai-conformance.yml edits as step files,
corpus files and test tags, and every other file as the branch has it.

- 8b9a32067 channel-lineup §7.4: a channel's breaks marked in its HLS Media Playlist, from its signed schedule
- af5947f31 route-record §6.3: a delivery's decision records why it took the route it took
- 558a6764f channel-lineup §7.4: a window ends before the year 10000, and every rule of the cue tags has a vector
- c42fa8c9c route-record §6.3: a control for each member that binds a decision to its documents, and what the chain proves

Co-Authored-By: Claude Opus 5.5 <[email protected]>
Sign in to join this conversation.
No reviewers
No labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
Transaction-Science/open-standards!636
No description provided.