extensions/sink-predicate.md registers the record `wai.delivery.sink_predicate` (`wai-sink-predicate`): a source's signed predicate travelling beside a programme, the registered form of unidirectional-delivery §2's sink-evaluated predicate. It carries the programme's blackouts (audience-decision §4.2) and its ad slots, each a list of candidate ads in the order the source prefers them, with the regions each is shown in and its frequency caps (a counter, a limit, a window), and declares exactly the state classes it reads (`location`, `exposures`); it is a route-record document signed in its canonical form under `wai:sink-predicate`. A sink evaluates it from its own state alone: the programme's verdict is audience-decision §4.6's at its aggregated location (unresolved, every blackout applied, when it holds none), and when that is `show` it fills each slot itself, taking in order the candidates shown at its location (a country of which a candidate lists only a subdivision is shown neither `only` nor `except`), whose caps its own exposures have not reached in their windows, with those the earlier slots and candidates added, and that fit the avail. A sink that keeps no exposures is shown no capped candidate. The evaluation causes no request and nothing about the device leaves it. A predicate holding, at any depth, a member by which its evaluation would send something away (`report`, `beacon`, `callback`, `uri`) or count across devices (`household`, `account`) is refused `predicate_needs_report`; a sink's log of a slot that presents a candidate whose cap was reached is refused `predicate_cap_breached`, one not shown at the location `predicate_not_targeted`, and any other departure from the fill `predicate_fill_mismatch`. unidirectional-delivery §2 and audience-decision §1 and §6 name it. live-compose §5.10 adds per-session ad tracks: in a squeeze or a double box, a session's sink may show the session's own ad, decided by a stitched-presentation pod-input record of that session, on the session's ad track (owner `ad_` and the session), bound to the composition by an ad-track binding, `WAT1`, signed by the record's signer under `wai:live-compose-ad-track`. A binding that does not match its session's composition is refused `ad_track_mismatch`, naming what differs: a descriptor that is not an ad frame, a record other than the one it names, a shared record or another session's, a record whose frame is not the descriptor's (layout, canvas, corner, window, gap, aspect), or a slot the record does not have; one signed by another key `ad_track_not_permitted`; one out of its bounds `ad_track_invalid`. While a binding is in effect (from `start` for its ad's duration), the session's layout is §5.9's with the track in the ad tile, the program where the room's frame puts it, and the room's ad participant with no tile and no voice. live-compose's §10 table, its descriptor and its receipt are unchanged; its codes for the binding are §5.10.5's. stitched-presentation §4.6 and §6: a composited break whose ad region shows each session its own ad is decided by a record of each session under the room's ad frame, never by a shared record, and the session's claim is that record's one presentation; no record, claim or verification rule changes. The references are the `sink_predicate` module of wai-rs (feature `sink_predicate`) and the `compose_ad_track` module (feature `compose_ad_track`). The corpora are live-conformance/sink-predicate/ (68 predicates, 23 evaluations, 18 logs) and live-conformance/compose-ad-track/ (36 bindings, 17 layouts); sink_predicate_verify.py and compose_ad_track_verify.py are their second implementations, which agree on every case, fail every mutated copy (17 of 17, 13 of 13) and catch every weakened verifier (35 of 35, 21 of 21). Nineteen faults planted in the reference each fail its tests, each matched by a check the second verifier drops: a cap breach (the fill ignoring caps, the log check ignoring them, an exposure a whole window old still counted, a sink that keeps none admitting a capped candidate); a predicate needing a report (the check removed, read only at the top level, the across-device members dropped); a per-session ad track not matching its session's composition (another frame, another session's record, a shared record, a descriptor that is no ad frame, a missing slot, another signer, the track kept at its binding's end); and a signature over the wrong bytes (a predicate's without its domain, under the audience decision's, over the text as written; a binding's without its domain and under the pod-input record's). Co-Authored-By: Claude Opus 5.5 <[email protected]>
2 KiB
live-conformance/sink-predicate — sink-evaluated predicates
The corpus of extensions/sink-predicate.md §7, in one file,
vectors.json.
Provenance. Generated here by wai_sink_predicate_vectors gen (wai-rs, feature
sink_predicate), from Ed25519 keys on seeds of one byte repeated 32 times and constant instants;
no input from outside this repository. Every region is an ISO 3166-1 user-assigned code (XA,
XB, …) or a subdivision of one, and names no place; every creative digest is the SHA-256 of its
ad's name.
keys names every key by its role, as 64 lowercase hexadecimal digits: the source that signs the
predicates, other_source, and small_order, the identity point.
predicates(§3.4, §3.5) are each a predicate'stext;outcomeisread, with itsdigest,source,programmeandstate, orrefuseand itsreason.evaluations(§4) are each a predicate'stextand a sink'sstate: itslocation(a region code ornull), itsexposures(each acounterand an instantat, ornullfor a sink that keeps none) and itssources;outcomeisevaluated, with theverdict, the blackoutsapplied, thealternateand, for each slot filled, itsslot, itsadsand itsrest_ms; orrefuseand itsreason.logs(§5) are each a predicate'stext, the sink'sstatebefore the slot, theslotand thelogof ads it presented;outcomeisconforms, orrefuseand itsreason.
Everything is synthetic: keys attest nothing. Each case states the outcome it was written to have, declared in the generator and never copied from a verifier.
wai_sink_predicate_vectors verify and tests/sink_predicate.rs check that the file regenerates
byte for byte and that wai-rs reaches every stated outcome. ../sink_predicate_verify.py is the
second implementation, written from the extension's text, which also mutates copies of the corpus
and drops each of its own checks in turn and requires each to be caught.