wai: channel lineup and guide — a linear channel's feeds imported, signed (wai.video.lineup), and its guide checkable #540
Loading…
Reference in a new issue
No description provided.
Delete branch "wai/fast-channel-guide"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Updated after #539 (the revenue share), main at
7473e6d7eorigin/main(3fb4e67dd). Both sides kept inCHANGELOG.md,SPEC.md§5,catalogue.rs,dx/mod.rs,tests/cli_ops.rsand the two workflows;WAI_FEATURES, the union test build and the features-alone list each holdrevenue_shareandchannel_lineup. The generated files (wai/catalogue/ops.json,wai/registry/capabilities.json,wai/vocab/v2.ttl,REGISTRY.md's table, Studio'sactions.generated.js) were taken from main and regenerated from the merged sources (wai registry export,wai ops export --out,node scripts/actions.mjs), not merged as text. The channel lineup's--dry-runsample intests/cli_ops.rsnow writeschannel-operator.key: the revenue share's sample writesoperator.keyin the same directory, andquickstart,revenue_shareruns both.3320d5b59).channel_lineup::receipt_to_json/receipt_from_jsonare gone: the import writes a receipt withChannelReceipt::to_jsonand verification reads one withChannelReceipt::from_json(main's, revenue-share §4.1, "the one JSON form of a channel receipt");signing_payloadstays private as on main and this branch calls main'spayload(), sowai_video_channel.rsis main's file unchanged. channel-lineup §5 keeps its number and is now a normative reference: the carriage is revenue-share §4.1, the leaves, root, signing payload, signature and identity §4.2, the rules §4.3 rules 1 to 5 and the signature rule 6; §1, §8.2 and §11 point there.channel_lineup_verify.pyreads receipts withrevenue_share_verify.py'sread_run,run_payload,run_identity,run_verifies,leafandmerkle, and no longer takes anything fromlive_ads_verify.py.sample.channel.json,media_rss_loop.channel.json,aired-misreported.channel.json; the two receipts that verification vectors edit,receipt_malformedandreceipt_bad_signature; and the sample re-sealed with each energy class:ModelBased,Estimator,OnChipCounter,CalibratedInstrument), the old reader andserde_json+ChannelReceipt::from_jsongave equal results, the old writer andto_jsonequal JSON values, and the indented files were byte-identical, each unedited one equal to its bytes on disk. Output:CARRIAGE-EQUIVALENCE: 9 receipts (8 read, 1 refused by both readers); readers, writers and files identical. After the change,wai_channel_lineup_vectors genchanges no file of the corpus (0 files differ; nothing to pull back).88deef467): with §5 now linking to revenue-share,check-wai-section-refs.pypaired the bare[of an interval written[a, b)with that link; each span is now written from its start to its end.What this adds
A linear channel's feeds, as operators exchange them, become signed WAI records, and the channel's guide becomes checkable against them. One new record,
wai.video.lineup(WLU1); thewai.video.channelreceipt keeps its signed bytes.wai/extensions/channel-lineup.md("Channel Lineup and Guide"): §3 the bounds, text values, the XML subset (stitched-presentation §3.5 plus one document type declaration that defines nothing) and the XMLTV and HLS time forms; §4 the import; §5 the receipt's JSON carriage (revenue-share §4, by reference); §6 the lineup; §7 the guide; §8 verification; §9 the operations and a worked example; §10 the reasons (30 added, plus live-events'bad_keyandbad_signature); §11 conformance; §12 security and privacy.episode-numsystem,wai-asset-idby default), Media RSS catalogues (each with its owner; items alone play back to back from a start), or a JSON form of both (wai-channel-feed), and the SCTE-35 date ranges of the channel's HLS Media Playlist (an out/in pair across two tags of oneIDmerged; duration fromDURATION,END-DATEorPLANNED-DURATION). Breaks split their programme into program and ad entries. It yields the receipt (unmetered, a grant reference with no ceilings), the lineup, and one live-events §4.3cuepayload per break, itscue_idasplice_insert()'ssplice_event_id(SCTE 35-1 §9.6, §9.7.3) or a hash of the break id. Refused, each with its reason, in a stated order (§4.10): a gap, an overlap, a programme with no stop, a time zone named by letters, a malformed time, a break outside its programme or of no duration, a window outside the schedule, an unknown or duplicate asset, a missing owner or artwork file, an SCTE-35 section that does not read, an oversized document or count, and a document outside its form.tv'ssource-data-urlasurn:wai:guide:<root>:<lineup>:<from>:<to>) and a now/next document (RFC 8785,wai-now-next), both pure functions of the receipt and the lineup. Verification (§8) recomputes and compares:guide_entry_unscheduledfor a guide that places a programme where the schedule does not,guide_mismatchfor any other difference, and for each programme whether the receipt's program-as-run says it aired.channel_lineup(video_channel,stitched_presentation; no new dependency), modulesrc/channel_lineup.rswithimport.rsandcorpus.rs; it writes and reads receipts withChannelReceipt::to_json/from_jsonand splits a bad signature from a broken rule withChannelReceipt::payload();wai_video_channel.rsis unchanged.quickstart, the shippedwai's build, now holdschannel_lineup(it already held every feature it depends on).channel.import,channel.guide,channel.verify(wai channel ...), placed local, with--json,--dry-runon the two that write,--yesbefore a file is replaced, and a refusal's reason as the error's code with exit 1.ops.jsonand Studio's generated actions follow; no Studio binding is needed (local placement).wai/live-conformance/channels/, written from fixed seeds bywai_channel_lineup_vectors gen: a sample channel (XMLTV beside another channel's programme, two owners' Media RSS, a Media Playlist with realsplice_insert()/time_signal()sections and CRCs, the same channel as the JSON form, SVG artwork), 27 import vectors (3 that yield, 24 controls, each the sample with one stated edit) and 22 verification vectors (6 verified, 16 controls).wai/live-conformance/channel_lineup_verify.pyis the second implementation, from the text alone.wai.video.lineup; wire identifiersWLU1,wai-channel-feed,urn:wai:guide:,wai-asset-id,wai-now-next) and its export, SPEC §5 paragraph, CHANGELOG entry, the conformance workflow step, the feature infeatures-alone, and the catalogue workflow's path for the corpustests/cli_ops.rsnow reads.Design decisions
channel-lineup.md(the brief's): the lineup is the one record it registers; the import produces it and the guide is rendered from it.EXT-X-CUE-OUT/CUE-INtags (no HLS text defines them), interstitial date ranges (stitched-presentation's), elements undermedia:groupother than the content, and anything the text does not name. Named in §1.WLU1:WCL1, first chosen, is live-recording's link record; the registry's test caught it.CHANGELOG.md,SPEC.md§5,catalogue.rs,ops.json, the conformance workflow andtests/cli_ops.rsis its own block, so a parallel branch's additions merge beside it; the shared single lines (WAI_FEATURES, the union test build, the features-alone list) hold both features after the merge.Checks (all on the Framework box,
[email protected] rb, unless marked local)wai/wai-rsunless noted)cargo test --locked --no-default-features --features channel_lineup --lib channel_lineup::cargo test --locked --no-default-features --features channel_lineup --test channel_lineupcargo test --locked --no-default-features --features video_channel --lib wai_video_channel::cargo test --locked --no-default-features --features revenue_share --test revenue_sharecargo run --features channel_lineup --bin wai_channel_lineup_vectors -- verify ../live-conformance/channelspython3 -I live-conformance/channel_lineup_verify.py live-conformance/channels(inwai/)cargo test --locked --no-default-features --lib cataloguecargo test --locked --no-default-features --lib codecs::registrycargo test --locked --no-default-features --features quickstart --bin wai --test cli_ops; also--features quickstart,appand--features quickstart,generate,score,staged,video_int,audio_int,splat_intand--features quickstart,revenue_share--test cli_opscargo run --no-default-features --bin wai -- ops export --check ../catalogue/ops.jsoncargo run --no-default-features --bin wai -- registry export --checkcargo run --no-default-features --bin wai -- breaking --against 7473e6d7e(origin/main)cargo check --locked --no-default-features --features channel_lineup --testsWAI_FEATURES,channel_lineupappended)cargo test --no-run --locked --no-default-features --features "$WAI_FEATURES" --lib --test channel_lineup --test revenue_share --test stitched_presentationcargo clippy --no-default-features --features channel_lineup --all-targets;cargo clippy --no-default-features --features quickstart --bin wai --tests;--features quickstart,revenue_share --bin wai --testscargo metadata --locked --offline --format-version 1python3 -I live-conformance/<x>_verify.py <dir>line of wai-conformance.ymlrevenue_share_verify.py;bearer_verify.pyneedscryptography, absent there, so run locally: pass;dred_verify.pytakes a built artifact and was not run (reads nothing changed)node wai/wai-studio/scripts/actions.mjs --checkcheck-tests-in-ci.py,check-workflow-concurrency.sh,check-spec-refs.py,check-wai-section-refs.py,check-no-vanity-metrics.sh,test-public-text-checks.sh,check-comparative-prose.sh,check-orphan-sources.pyruby -ryaml ...over.github/workflows/*.yml;wfsteps.py HEAD b929c31ae 7473e6d7e(the two merged refs)564a42df8: each is exactly the union of the two sides' line changes (this branch's four--binlines and--test channel_lineup, main's--test revenue_share; both features in features-alone), nothing elsecheck-public-names.shover the 43 files this branch changes against main;grep -Eof the patterns over every commit message and this bodyFalsifications (each guard mutated on the build host, run, restored with
git checkout --; all rerun at the merged head88deef467: 18 of 18 caught, no anchor missed)verify_signatureaccepts any signaturetests/channel_lineup.rsfails (exit 101)lineup_programme_mid_entryreceipt_verdictskipsverify()receipt_invalidcue_idfromsplice_insert()(§4.8)import_gap's row removed--mrss-owner owner-bbecomesowner-zChannelReceiptbytes unchangedwindow_end_mswritten little-endianlegacy_hwshunt_receipt_keeps_its_signed_bytesfails (101)guide_mismatch)import_gap is not a reason of §10)--dry-runchannel.importwrites withstd::fs::writecli_opsdry-run test fails (101)One falsification found a real defect: breaking the
splice_event_idpath of §4.8 was not caught at first, because the corpus generator wrotesplice_info_section()one byte short (4 bytes for the 40-bit encryption/pts_adjustmentblock), sosplice_command_typelanded on byte 12 and every cue took the hashed id; both implementations agreed on the wrong bytes. Fixed (b929c31ae) and pinned by a unit test that reads SCTE 35-1 §14.2'ssplice_insert()sample (splice_event_id0x4800008F) and §14.1'stime_signal()sample. A second, from the Python verifier's dropped-check loop, showed the count bound, whole-second boundaries and break/break overlap had no control; and planning the Rust falsifications showed the one §6.5 control was refused by two rules at once. Each now has its own control.Not in this PR
Revenue share (FAST-B); ad decisioning; playout; SCTE 224 audience or blackout rules (a lineup states where an asset may be shown, not when);
EXT-X-CUE-OUT/CUE-IN; fetching artwork over the network (the import hashes files it is given); signing the cues as room events (an import yields the payloads; a room's principal signs them); Studio panels (the operations are local); any deploy, tag or release.🤖 Generated with Claude Code