### The integer codecs' payloads pinned in place: `WIV1`, `WIH1`, `WIA1`, `WIS1`

- **Pinned in place** (SPEC §5, *Payloads of the WAI-native integer codecs*, and the
  new extension [integer-payloads](extensions/integer-payloads.md)): the payload of
  `wai.video.int_motion` is `WIV1` with keyframe 0; of `wai.video.int_hyper`, `WIV1`
  with keyframe 1, a nested `WIH1`; of `wai.neural.int_hyper`, `WIH1`; of
  `wai.audio.int_codec`, `WIA1`, which carries the sample rate; of
  `wai.splat.int_codec`, `WIS1`, which carries each attribute's presentation range.
  These strings' rows named a decoder and no byte layout; three of them
  (`wai.video.int_motion`, `wai.audio.int_codec`, `wai.splat.int_codec`) also named
  the tool that wrote a JSON bitstream, which SPEC §5 called the bytes the codec
  normally emits. So the pin changes what these strings carry. SPEC §8 gains
  *Completing a registration*, which permits it for a WAI-native string whose row
  fixed no byte layout and lists the envelopes it makes non-conforming; from this
  revision their formats are fixed as SPEC §8 states. No new string is registered.
  `codecs::PINNED_INT_PAYLOADS` is the one table of strings and magics.
- **The JSON is a converter input, never a payload.** The encode and export tools
  still write JSON; `int_payload::{wiv1,wih1,wia1,wis1}_from_json` convert it and
  state what it left implicit (an absent `mv_precision` is 1; the first frame carries
  no motion vectors; a short motion-vector list is padded with `(0, 0)`; the audio
  sample rate comes from the export's `audio.json`; a splat range must be exactly a
  binary32 value; the JSON `afrac` must be the model's; the channel count must match
  the medians, tables and offsets). *Envelopes this makes
  non-conforming:* a `WAI1` or `WAI2` that carries anything but the pinned format
  under these strings, a tool's JSON output among them. It still parses; a sink
  refuses it at decode (a JSON form with `bad_magic`) and does not try the fallback.
  Every emit path refuses to write it: `Wai::to_emit_bytes` and
  `WaiMulti::to_emit_bytes` (so the C packers `wai_envelope_pack` and
  `wai_envelope_pack_multi`, `WAI_ERR_INVALID_ARG`), `wai-web`'s `pack` and
  `packMulti`, and `wai wrap`. They run `container::check_int_payload_framing`, which
  parses the payload with the wire parser a sink runs and the capability's keyframe
  kind, so it checks every parse rule of the extension's §2–§6 and leaves to the
  decode only what needs a parameter set or the streams (§7); `wai inspect` reports
  what it finds. A refused emit is the new `EmitError::IntPayload`. An encrypted
  envelope's payload is ciphertext (SPEC §6), so the emit paths skip it and a sink
  checks the plaintext after decrypting (SPEC §6.4); `wai inspect` says so. No
  committed envelope or conformance corpus carried such a payload. The reference
  dispatchers (`Wai::dispatch_verified`, `WaiMulti::pick`, and so the neural sink,
  its C ABI and `wai-web`'s `dispatchCapability`) implement SPEC §6.4 step 2: they
  open no scheme, so an envelope whose manifest declares an encryption other than
  `"none"` is inert at them (`unsupported_encryption`; `container::Inert::Encrypted`,
  `container::Skip::Encrypted`, `neural::DecodeError::Encrypted`), and a payload sent
  in the clear under a claim of encryption is not decoded as plaintext. A `WAI2`
  manifest's `security` member (now stated in SPEC §7.1) must be a security object to
  be written (`EmitError::Security`, `container::manifest_v2_security`); one that is
  not is read as encrypted.
- **One integer decode path.** `int_payload::decode_int` decodes the strings above
  and `wai.neural.int_synth` and `wai.neural.int_mlicpp` from parameter files held in
  memory, with no ONNX runtime and no filesystem, natively and for `wasm32`, through a
  dispatchable capability only. A refusal is one code from a closed list
  ([integer-payloads](extensions/integer-payloads.md) §8). `int_payload::resolve`
  verifies a pin over sets held in memory and runs SPEC §3.1's check 5 for a pinned
  decode and over the set an unpinned decode reads. The neural sink delegates every
  integer capability to it: integer video, PCM and splat attributes come back as
  `neural::Decoded::Int`, never converted; `ModelRegistry::builtin` advertises
  `wai.video.int_motion`, which reads no set; the C ABI refuses an `Int` output. A
  capability this build has no integer decoder for (`unsupported_capability`) moves
  dispatch on to the fallback, as SPEC §4 treats a capability a sink cannot decode.
  Check 5 covers every identity of the bytes a decode reads (SPEC §3.1): after a pin
  verifies, it runs again over the listing of the members read and a one-file set's
  file digest (`int_payload::read_identities`), so a set revoked under one identity
  is not reached through a pin naming it by another; and a listing beside an
  unpinned set counts only when it matches the files read. Outside the integer path,
  at a sink that holds a key log, an unpinned decode reads what it can read once —
  the registered file of a one-file set, or every file below the directory its
  decoder reads from (`pin::read_dir_set`) — and check 5 runs over every digest a pin
  over it could name: the file's, or the directory's derived listing
  (`pin::listing_of_dir`) and each file's, and each listing beside them that matches.
  The decode reads only those bytes, never the path again; where the key log holds
  the capability's sets to jwp-receipts §7.9, only the part §7.9 authorised (the
  whole, the files a granted listing names, or one granted file). So a revoked set
  is refused with no listing beside it, a granted listing authorises the files it
  names and nothing else, a file changed after the check is not read, and a file the
  decode needs outside the set is `set_incomplete` (SPEC §4, *Module authorisation*).
  `pin::PinAuthority` gains `requires_grant`, with a default.
  *Breaking:* `neural::Decoded` gains the variant `Int` and is now `#[non_exhaustive]`,
  so a `match` on it outside this crate needs a wildcard arm; `neural::ParamSource`
  gains `UnpinnedRead` (an unpinned integer set read into memory once) and
  `Checked` (what an unpinned decode at a sink with a key log reads, read once) and is
  `#[non_exhaustive]` too; `int_payload::IntParams` is opaque, built by
  `int_payload::resolve` (or `IntParams::none()`).
- **Checked decoders.** `int_video`, `int_audio` and `int_splat` decode a typed
  stream that the binary and JSON forms both read into. Every size is capped at
  `2^26` elements (a `WIV1` clip at `2^30` bytes and `2^20` frames, its motion-vector
  field at `2^26` elements) the same on every target, every network's shapes are
  planned before anything is allocated, every latent and layer is checked for
  exactness, and a stream that runs out is refused at the first symbol it cannot
  give, so a build with overflow checks and one without accept the same bytes. A
  frame, a clip or a latent grows only as its symbols are decoded, to its declared
  size and no further, so a short stream stops early; `int_video` holds frames as
  RGB8. A stream that carries its symbols is decoded in full, and a few kilobytes can
  code a clip at the cap, so the header bounds the work: a sink reads the declared
  cost before decoding (`int_payload::declared_cost`, `container::declared_size`, the
  C ABI's `wai_int_payload_declared`, `wai-web`'s `intPayloadCost`) and applies its own
  size policy (integer-payloads §7, *Memory and work*). The cost counts the networks'
  multiply-accumulates layer by layer, exactly (`DeclaredCost::macs`, a `u128`;
  `macs` on `intPayloadCost`, a `BigInt`). The reference sink applies one:
  `neural::ModelRegistry::with_cost_limit` refuses a payload whose declared cost
  exceeds the limit before any symbol is decoded, with the new code `cost_over_limit`
  (`int_payload::check_cost`, `neural::DecodeError::OverCostLimit`), and dispatch moves
  on as for a capability the sink cannot decode (§8). `neural::declared_cost` reads the
  cost over the set a registry resolved, pinned or not. The C ABI's
  `wai_int_payload_declared` takes the set's path (or null for the header alone) and
  gives the working bytes and the multiply-accumulates too (`wai_int_declared_t`
  gains `working_bytes`, `macs_lo`, `macs_hi` and `cost_known`); its neural decode
  entry points no longer decode integer video, PCM or splat attributes, which they
  have no output kind for, and refuse them before decoding. A `WIA1`'s latent may be no
  longer than its `t_out` needs (`geometry_mismatch`). The JSON reader
  (`int_video::VideoJson`) caps every size before it pads a motion-vector list, and
  reads a field that does not fit the target's `usize` as absent rather than
  truncating it. In the browser, `decodeWiv1` keeps the decoder's frames
  (`DecodedVideo.frame(i)`) instead of copying the clip into one buffer.
- **One refusal order.** The extension states the order of a decode's checks
  exactly (§7): the set's members, the set against the payload, every network
  planned with its caps, the shapes, then each stream symbol by symbol, a run-out
  before the value it would give. Networks are planned layer by layer in 64-bit
  arithmetic (`int_hyper_synth::plan`, the audio synthesis's plan), a size past 64
  bits being past the cap, so a 32-bit and a 64-bit sink refuse a malformed network
  with the same code; a planned network's parameter lengths are checked before any
  symbol is decoded, and a convolution's padded side is capped. A set's entropy
  tables obey §2's length rule (3 to 4097 entries). The parser reads a table of one
  per channel table by table. A `WIV1` keyframe's planned size is checked against the
  clip's `h × w` before any of the keyframe's symbols is decoded, so
  `int_payload::declared_cost` and the decode refuse the same bytes with the same
  code; the Rust verifier checks that for every fixture. Fixtures pin each case.
- **One header for a one-frame clip.** A `WIV1` of one frame codes no motion
  vector, so its `mv_precision` and `block` must be 1 (`field_range` otherwise); the
  converters write them so. The extension's §2 says a payload's hash identifies the
  payload, not its output (unused table entries, say, code the same clip in other
  bytes), and that one output is identified by its decoded digest (a clip's
  `clip_hash`).
- **Canonical streams.** After its last symbol, each rANS stream of these payloads
  must have been read to its last word and end in the state `2^31`, where an encoder
  starts (`rans::RansDecoder::is_canonical_end`); otherwise the payload is refused
  with the new code `stream_noncanonical`. An unread word or another starting state
  coded the same symbols in other bytes. Every committed stream, the JSON forms'
  included, ends canonically. `int_hyper_synth` gains
  `decode_conditional_checked`; `int_synth` refuses a side of `2^64` or more instead of
  shifting by it.
- **Parameter sets.** SPEC §5 names the members the integer decoders read:
  `g_s.whm`, `h_s.whm` and `entropy.json` for `wai.neural.int_hyper` and
  `wai.video.int_hyper`. `wai.audio.int_codec` (`model.wam`) and
  `wai.splat.int_codec` (`g_s.whm`) are registered as one file, which widens their
  registrations (SPEC §8). `wai registry export` writes nothing new: no exported field
  records a set's shape. A `WHM1` or `WAM1` network holds nothing after its last layer
  (`IntModel::from_bytes` and `AudioModel::from_bytes` refuse trailing bytes; every
  committed network has none), so one network has one file and one digest.
- **Receipts.** A `wai.video.int_motion` receipt identifies its `WIV1` payload
  (`VideoReceipt::seal_decode_wiv1`, `verify_reconstruction_wiv1`), and
  `wai_video_meter` seals over it. The entry points that read the JSON form are
  deprecated; on a conforming JSON bitstream their output is unchanged, so every
  receipt sealed over a committed one, and every pinned digest of one, still
  verifies. They now read the JSON through `int_video::VideoJson` and the checked
  decoder, so they refuse JSON the old decoder took: a stream that runs out (it
  decoded on from the exhausted state) or does not end canonically, a table that
  breaks the table rules, `q` outside 1..=65535, an `mv_precision` other than 1 or 2,
  other than one motion-vector list per frame, vectors on the first frame or more
  vectors than blocks, a vector outside `i16`, a zero size, and a size past the caps. jwp-receipts §2.1's object content hash moves to its own
  module and feature (`jwp_hash`), which `receipt_claims` enables;
  `claim_receipt::object_content_hash` re-exports it, and no corpus changed.
- **JSON readers that refuse.** `IntModel::try_from_json` and
  `CondBitstream::try_from_value` read the conditional codec's debug JSON without
  panicking; `wai-web`'s `decodeIntHyper` and `decodeIntHyperBin` read it through
  them, `CondEntropy::try_from_value` and `decode_conditional_checked`, so an empty
  model or bitstream, or sizes past the caps, are refused. `int_video::VideoJson`
  refuses a frame whose motion vectors are not a list.
- **Browser.** `wai-web` decodes the four formats (`decodeWiv1`, `decodeWih1`,
  `decodeWia1`, `decodeWis1`); the entry points that read the JSON forms are for
  debugging and conversion.
- **Conformance.** The payloads sit beside their JSON sources in
  `wai-rs/tests/vectors/`, written by `wai_int_payload_vectors`, with refusal fixtures
  and an index in `int-payload-conformance/`; `int_payload_verify.py` checks them
  independently in Python's standard library and decodes the `WIV1` keyframe-0
  payloads in full; `byte-exact-conformance` parses and decodes the `WIV1` and `WIH1`
  payloads on every leg of its matrix. CI runs the new tests in both build profiles,
  the generator in the corpus loop, the verifier, a `cargo check` of each integer
  feature alone, and a `wasm32` check.

