- Past WALK_BOUND, a revision ahead of the head (its media sequence after its last
complete segment greater) re-anchors the chain (reanchored): it becomes the head,
the chain keeps its bindings, the revisions between are not signed. One not ahead is
refused (playlist_receipt_walk_exceeded), so a stale or older revision never moves
it back; the head fetched again is unchanged, however often. (§8.6)
- Bindings key a line by its URI resolved against the playlist's URL and normalized
(RFC 3986 §5.2, §6.2.2; http_binding::uri, and an independent rfc3986_resolve in
http_verify.py), so ./720/1201.0.wai is 720/1201.0.wai. (§11.5)
- A gap at a sequence number bound to an object is playlist_listing_rebound.
- A binding is kept while one of the last 256 revisions lists it (RETENTION); a
rebinding older than that is not detected, as §11.5 states.
- Corpus: live_edge_reanchored*, live_edge_reanchor_not_backwards,
live_edge_parent_substituted, live_edge_parent_invalid, live_relisted_dot_alias,
live_dot_alias_same_object, live_gap_withdraws_a_segment, and uri_resolve cases (RFC
3986 §5.4 and alias spellings) with a fuzz family.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Appendix F's Budget paragraph rewritten: the highest option at or below the policy's
choice whose labelled figure is within the budget (within, lowered), else option 0
(unmet, replacing floor); not applied only when no option is labelled. Rust and
http_verify.py; the reviewer's three rows as vectors (abr_budget_not_raised_*), and
a unit test that no choice under a budget is above the policy's.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
dash_read never expands @r: a timeline is its runs (t, d, count), a run's end checked
against 2^64 - 1, and §11.2's comparison walks the runs of both timelines, each step
passing one. A document holds at most 256 timelines. Attribute names are kept in a
set. http_verify.py does the same. DashTimeline::from_mpd expands only up to the
caller's own segment count. tests/dash_read_bounds.rs holds the reader to the
document's size under a counting allocator (32 timelines of 2^20 segments) and an
element of 40000 attributes to linear thread CPU time; http_verify.py's self-test
holds both with tracemalloc and thread_time. Appendix I states it.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
MAX_OPTIONS had reached include/wai.h with an unrelated commit (25ae3576); it and
MAX_FILE_NUMBER are cbindgen:ignore, like the walk bound and the MPD reader's limits,
and the regenerated header is main's again.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
U2's final catalog.rs with its checked segment times, its MoQ catalog (the depends
rule kept), its stage view and render_all, written through wai_media_playlist,
render_multivariant, render_hls_with and render_mpd; the DASH timeline through
dash::scaled_timeline, the checked multiply in the shared renderer. Goldens
regenerated by wai-pack conformance regen: they are U2's but for the classical
master's variant URI, ../classic/floor-<c>.m3u8, which makes each class's master the
classical one with the session-data line alone (http-delivery §11.1), and the
LISTING hashes that follow from it.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
After the first line and the EXT-X-VERSION and EXT-X-INDEPENDENT-SEGMENTS lines that
follow it, before any other line (§11.1); a line between an EXT-X-STREAM-INF and its
URI, before the version line or at the end is classical_changed, in Rust and
http_verify.py. insert_session_data writes it there; corpus cases for each place.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
ManifestReceipt::from_json refuses a member name repeated in any object, names
compared after their escapes are read; http_verify.py refuses the same through
object_pairs_hook and refuses an unpaired surrogate as serde_json does. Corpus cases
and receipt fuzz edits for each; receipt_diff.py gives no disagreement.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Appendix F states each input's bounds (its field's type, at most 256 options and
layer objects, depth sums below 2^64) and refuses beyond them
(abr_state_out_of_bounds). Within them every quantity is exact: throughputs and rates
in 128 bits, the look-ahead without saturation, and the utility-over-buffer
cross-multiplication widened to 192 bits. Under a budget only an option with a
labelled figure within it is chosen; not_applied only when no option has a labelled
figure. Vectors for each budget outcome, the extremes of every type and each bound.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
wai http mpd takes the classical MPD's period, @start, @timescale, t and d
(--classical-mpd), leaves a gap segment's S and SegmentURL out, and checks the result
adjacent. dash_read reads the XML subset of the new Appendix I and an MPD's timelines
(@r expanded, timescale inherited), and check_dash_adjacent holds §11.2 in DASH.
render_mpd escapes tab, LF and CR as references and refuses a character outside XML;
a Period's @start is in milliseconds and its @id optional; scaled_timeline is the
checked segment-time multiply the packager uses. Corpus: a classical MPD at 90 kHz,
wai-gap.mpd, dash_adjacency cases, MPD id cases, and an MPD-pair fuzz family.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Every playlist receipt is served at mr/<receipt_hash>.json too. LiveChain holds the
head and what its receipts bound: the head fetched again is unchanged; any other
receipt chains when the walk by parent links reaches the head, each parent served,
read, named by its link, verified and under the head's key, at most 64 back
(playlist_receipt_parent_missing, playlist_receipt_walk_exceeded). Bindings keep each
URI's and media sequence number's cid across revisions and receipts
(playlist_listing_rebound). Corpus cases for the reload and skip an origin's replay
gives, the bound, a missing parent, a receipt chained by hash under another key, and
an alias and a sequence number rebound; http_verify.py holds each.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
A canonical key of large order, S < L, R of large order compared byte for byte.
Corpus receipts at the edges: the identity key in both spellings, R the identity
canonical and respelled under the key B, and a signature under B the rule accepts.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
One HLS and one DASH renderer for WAI: wai-pack's floor playlist, masters, WAI
multivariant and media playlists and both MPDs are now written by
wai::hls_render::render_hls_with, http_binding::hls::{wai_media_playlist,
render_multivariant} and http_binding:💨:render_mpd; the WAI media playlist is
wai_media_playlist over the floor playlist, so its adjacency is checked as it is
written. Every golden byte is unchanged but one line, by the generator (wai-pack
conformance regen), for http-delivery:
- the classical master's variant is ../classic/floor-<c>.m3u8, which resolves to the
same playlist from every class's directory, so each class's master is the classical
one with only the session-data line added (http-delivery §11.1, §11.8: no other line
changes); before, each class's master rewrote the variant URI;
- a #WAI-LAYER line follows its segment's EXTINF, before the URI (http-delivery §10.4,
where its reader finds it); before, it followed the URI, where a reader takes it as
the next segment's. The golden lists no layer, so no golden byte moves for this.
packaging §8.3 and pack_verify.py say and derive the same; pack_verify.py holds
540/540, wai-pack conformance check holds every vector, clippy is clean under
-D warnings, and the catalog test reads the layered playlist with http-delivery's
reader, holds it adjacent to the floor's, and holds a class's master to the classical
one.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
wai-pack's catalogs move onto this branch's renderers in the next commit; W4-3 lands
first, after which a merge of main brings the same commits.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
wai-pack's catalogs (W4-3) are to render through these renderers, so they now express
everything its Mode N output holds:
- render_hls_with writes EXT-X-PLAYLIST-TYPE, EXT-X-INDEPENDENT-SEGMENTS and a header
EXT-X-MAP, and EXT-X-GAP before a segment's EXTINF; the reader reads them back, so
a classical floor playlist is in the rendered form too; the WAI media playlist takes
the classical one's type and independence, never its map, and marks a WaiSegment
gap; the playlist receipt's render and extras carry the new members;
- render_multivariant writes a classical or a WAI multivariant playlist: a version,
independent segments, session-data lines (VALUE or URI), and variants with or
without X-WAI-FLOOR; wai_multivariant is its WAI case;
- dash::render_mpd writes any static one-period MPD (content type, size, segment
alignment, startWithSAP, descriptors, an initialization, a dependency per layer, a
timeline per representation); wai_mpd is built on it, with minBufferTime and
Period@start in whole seconds as the packager writes them.
Where the texts differed, http-delivery decides: a variant's bandwidth counts the
layer objects listed with a segment and leaves out gaps (as the packager does; this
tree had left layers out), and a WAI1 is listed with n alone, h only for a WAI2
(§5.4). The corpus, the receipts and the Python verifier follow, with a gap playlist
and packager-line cases.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
wai http origin --live-edge PATH=REVISION,… [--live-step-ms MS] serves a playlist's
revisions one every MS from the start: a blocking reload (_HLS_msn, _HLS_part) is held
until a revision holds that segment or part, a part's alias until the revision that
first lists it with its cid, then answered with the object that cid names; a hint no
revision fills is not found once the edge ends. --live-swap serves another part's
object under an alias, which the listed cid refuses. A test replays the corpus's four
revisions and checks every answer by what it holds, not by a clock.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
http-delivery: §8.6 says where a playlist receipt is, what its rendering covers, the
public class, the order a client checks it in and the live chain; §10.4 where a layer
line sits; §11.1 how bandwidths are computed, the reference wai.mpd, and the published
classical master (classical_changed); §11.2 durations to the millisecond, rounded half
up and read exactly, parts compared where both list them, delta updates not compared;
§11.4 how a Live-P playlist is rendered; §11.6 when a part is listed and when signed.
New Appendix F (the reference adaptation policies, informative), G (the rendering a
receipt signs) and H (the receipt file, its hashes and signed bytes). §13 gains
playlist_malformed, classical_changed and the four playlist receipt reasons; Appendix
E records the HLS second edition's part, part-info, server-control and preload-hint
clauses as read (draft 22). SPEC's wai.video.manifest row names the render digest.
CI: the corpus loop, the fuzz build, the CLI tests and falsify/run.sh build with
video_manifest; wai-http.yml fires on the playlist sources. The depth case between
layer boundaries moves to 200 kb/s, where the 900 ‰ margin leaves it between them.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Eighteen patches, each breaking one function of the playlist side and naming the
cases that must fail: adjacency (another program date-time, a larger discontinuity
sequence), rendering (parts after EXTINF, layer lines after the URI), bandwidth
rounding, duration rounding, a delta update read, the session-data line before
#EXTM3U, the render digest left out of the signed payload, a receipt under another
name, an unchecked parent, the open segment's parts not read for cids, the integer
log without interpolation, a waiting utility rule, a look-ahead blind to stalls, an
unlabelled figure taken as zero, per-layer bytes as a depth's, and size-blind entry
order. falsify/run.sh builds with http and video_manifest and runs the eleven new
Python mutations. A depth case at 150 kb/s sits between layer boundaries.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
http_verify.py now implements, from the extension text, the media playlist reader and
rendering, adjacency, the WAI media and multivariant playlists and wai.mpd, the
session-data line and the unchanged classical master, playlist receipts (their file
read strictly, the segment leaves, Merkle root, class hash, render digest over JCS,
and Ed25519 verification written from RFC 8032, so the receipt cases still need only
the standard library), the live edge, and the four adaptation policies with their own
integer log; a sink's order may be a policy over the verified head. New mutations:
pdt-ignored, dseq-ignored, parts-after-extinf, average-floor, render-digest-off,
name-unchecked, chain-unchecked, ilog2-no-interp, bola-waits, unlabelled-as-zero,
round-half-down. Its layer-tag reader now takes Rust's ASCII white space (no vertical
tab), which it had not.
wai_http_vectors fuzz gains seven families (playlist_parse, adjacency, unchanged,
render, abr, ilog2, receipt) and the ops render and ilog2; the corpus gains ilog2
vectors. At seeds 1 and 2 the two implementations agree on every case; the
differential first found the Python reader taking -0 as an unsigned integer, now
refused as the Rust reader refuses it.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Objects (O10 is the HTTP link's layer set, so these take the next numbers): O11a,
O11b and O11bL1, a staged-delivery pair with a layer over the second (the design's
O10, layered segments); O12s0/O12s1 and 24 parts O12p<s>_<pp>, a low-latency set of
two 3.84 s segments of twelve 0.32 s parts, each part its own object decoded alone
(the design's O11); O13a-c, the player fixture's WAI segments. Their payloads are
int_motion clips encoded once by this tree's encoder and frozen in src/ with their
SHA-256; the player fixture's classical presentation (H.264 fragmented MP4, playable,
no tool name left in it) is frozen in players/classical/.
Generated: the WAI media playlists beside their classical ones (O9, O10 with two
layer lines, O11, four live revisions, the player fixture), the multivariant
playlists, two MPDs, the published classical master with its one session-data line,
and a playlist receipt for every media playlist under a published test key, the live
revisions chained. Cases (new ops media_playlist, multivariant, mpd,
playlist_parse, adjacency, unchanged, playlist_receipt, live, abr, abr_order, depth;
fetch and same_pick under a policy's order) hold rendering, reading, adjacency, the
unchanged master, receipt checks and chains, parts verified against the cids later
revisions list, and 40 seeded states per adaptation policy with the budget's every
outcome. The tool needs video_manifest for the receipts.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
The wai CLI writes a variant's WAI media playlist aligned with its classical one and
wai.m3u8 (union, floor, computed bandwidths) from an objects list, and wai.mpd on the
same timeline; seal-playlist writes mr/<manifest_hash>.json for a playlist in its
rendered form (with --parent for a live revision); check-playlist reports signed, or
the reason it is not, and checks the parent and adjacency. Without video_manifest the
two receipt commands say so. The origin serves .m3u8 and .mpd to be revalidated on
every use. hls::{WaiSegment, WaiPart, WaiLayer}::of build entries from envelopes, a
layer's N from its refines.layer.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
dash::wai_mpd writes a static MPD with one WAI AdaptationSet (application/wai, the
union as @codecs, the delivery EssentialProperty and the floor SupplementalProperty)
whose SegmentTimeline is the classical timeline, given or read from the classical HLS
playlist (DashTimeline::from_hls). playlist_receipt (with video_manifest) seals a WAI
playlist file in its rendered form under a public class (window = media sequence),
checks a playlist against mr/<manifest_hash>.json in a fixed order
(playlist_receipt_malformed, receipt_name_mismatch, playlist_receipt_mismatch,
playlist_receipt_invalid) and holds a live revision chain to one signer and its
parents (playlist_receipt_unchained). hls::listed_cid gives the cid a playlist lists
for an alias or an object name. The segment kind's leaf tag moves into the receipt.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Four policies over options of increasing size, re-implemented in integer arithmetic:
Throughput (the highest option under the last samples' throughput less a margin),
BufferBased (reservoir and cushion), BolaLike (utility over buffer; the log is
ilog2_q16, a 16-interval table interpolation in Q16; unlike the published algorithm
it never waits) and Mpc (every sequence over a short horizon, scored by utility less
stalls and changes). clamp_to_budget applies a joule budget only through figures a
receipt labels with their class (labelled_figure); a choice without one reports the
budget not applied. order_entries puts the choice first within each capability of a
WAI2 and keeps every entry; depth chooses a layer depth over a base.
The Selector's order now takes the verified head with its manifest, so a size-aware
policy (selector_order) gives the same order, and the same pick, on the range path
and the whole path.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
parse_media_playlist reads a classical or WAI media playlist (parts, open segment,
preload hint, #WAI-LAYER lines attached to their EXTINF or EXT-X-PART line, in
increasing N) with durations read exactly and rounded to the millisecond, half up.
check_adjacent and timelines_adjacent hold a WAI playlist to its classical one
(adjacency_misaligned). wai_media_playlist renders the WAI media playlist of a
classical timeline and the objects covering it (on demand, Live-S, Live-P with parts
under alias and cid, layers, the open segment and its hint) through render_hls_with,
and reads it back; render_inputs and is_rendered_form give a rendered playlist's
inputs back for sealing. wai_multivariant writes wai.m3u8 with the union, the floor
and integer bandwidths computed from envelope_len over duration (no NAME/GROUP-ID,
three-decimal frame rate). session_data_line and classical_unchanged
(classical_changed) keep the classical master to at most the one added line.
package_map relates the classical package's segment map by sequence number.
LayerTag is now the renderer's LayerLine.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
hls_render holds the segments and their HLS text, with no dependency, so the HTTP
binding renders WAI playlists alone: render_hls_with adds a media and discontinuity
sequence, a fixed target duration, partial segments with their server control,
discontinuities, program date-times, #WAI-LAYER lines (N only) and a preload hint;
under HlsRender::legacy with no extras it gives render_hls's bytes. check_render
refuses inputs that would not read back as themselves (playlist_malformed).
ManifestReceipt gains render and extras: verify and verify_render recompute the
playlist with render_hls_with, and the signature also binds a render digest (BLAKE3
over the JCS of render and extras) only when a rendering is present, so every
receipt sealed before keeps its signed bytes (a pinned pre-retrofit receipt still
verifies) and a rendering has one form. seal_with, to_json/from_json (strict) and
receipt_file_name (mr/<manifest_hash>.json) are new.
Co-Authored-By: Claude Opus 5.5 <[email protected]>