### Staged measurement and the session claim in the browser

- **The measurement core, bound for JS** ([staged-measurement](extensions/staged-measurement.md)).
  `wai-web` binds `wai::stage_measure` (`src/measure.rs`), the code `wai_stage_measure`
  runs, compiled to wasm32: `evaluateTitle` gives the text `eval --json` prints for a
  title (counted bytes, quality integers, first presentable bytes, the layering
  overhead's points, deduplication, the output tree), `simulate` and `simulateModel` the
  text `sim` prints, `countObject` one envelope's counted bytes or the code it is not
  countable with, `qualityRgb8` one picture pair's integers, and `outputRoot` the output
  tree's root. A title is read from a map of files; `titleMissing` names the next file it
  needs, so a page fetches them one at a time. Sums of squares are decimal strings, as the
  corpus writes them.
- **The session claim, sealed in the browser** ([jwp-receipts](extensions/jwp-receipts.md)
  §2.4.12). The claim classes (`receipt_claims`) build for wasm32 with the measurement
  core, and CI builds them so. `sessionBegin` records a session at its entry object
  (`wai::session_recorder::SessionRecorder`): the decoder that decoded each envelope, each
  presentation with its output's digest, each object received. `finish` seals the claim
  with a key whose seed the page supplies, checked against the record first; a browser
  session's `energy` is `null`. `verifyClaim` checks a claim against the presentations and
  the objects as the claim-conformance runner does.
- **Proved byte for byte.** `demo/verify_measure.sh`, run in the wasm job, requires every
  golden, refusal title, counted object, quality pair and simulator result of
  `staged-measure-conformance/` from the wasm32 build, and the native `eval --json` of every
  fixture to equal the wasm32 evaluation. A session recorded the way a page records one is
  the native tool's claim byte for byte and commits to the golden output tree; the wasm32,
  native and Python verifiers give the same verdict on it and on its altered variants.
- **A recorder that holds the envelopes checks the claim against them.** A sink hands
  `SessionRecorder` each object's bytes (`received_envelope`): the recorder derives its
  content hash, counted and transferred bytes, and whether an entry names it, and
  `seal_checked` checks the claim against every envelope it holds, as a verifier holding
  the `envelopes` context does, so an entry naming a capability its envelope does not give
  never leaves the sink (`session_source_mismatch`). An object the sink does not hold is
  `received_unheld`, by its content hash; the recorder no longer takes an object's figures
  as told. The native tool's session over a title and the browser's `SessionHandle` both
  hold their envelopes; `decoderFor` refuses a capability that is neither the envelope's
  nor its fallback and requires the decoder's `first_unit`, `received` takes the bytes
  alone — bytes that are not an envelope included, recorded as an inert object neither
  counted nor presented, so the session accounts for every byte transferred —
  `verifyClaim` takes the envelopes, and integers from JS are refused unless whole and in
  range.
- **An object is listed once, as its envelope gives it** (§2.4.12, the objects). No
  (content hash, path) is listed twice (`session_object_duplicate`): a sink that fetches
  an object again lists it once, its transferred bytes every byte of every fetch, as the
  recorder merges repeats. After the sums, an object is `presented` exactly when a decoder
  entry names it (`session_presented_mismatch`). With the envelopes, an object listed with
  a held envelope's content hash states that envelope's counted bytes, `null` exactly when
  it is not countable (`session_counted_mismatch`).
- **Envelopes no entry names are received, not refused.** Inert bytes — of neither
  magic, or whose `security` member is not a security object — are refused as unreadable
  only where a decoder entry names them (`session_envelope_unreadable`); those, and an
  encrypted envelope, no entry names are a received object, uncounted and not presented,
  and one listed with counted bytes, even 0, is refused (`session_counted_mismatch`). A
  page that receives such bytes lists them and seals its session.
- **The layers and units an envelope gives.** A decoder entry states `first_unit`, the
  unit at which its object's first unit is presented. With the outputs and the
  envelopes, each presentation through an entry whose envelope is held is at the layer
  count that envelope gives — 1 for a base or a rendition with no `refines`,
  `refines.layer + 1` for a layer (`session_layers_mismatch`) — and of a unit it covers:
  from `first_unit` plus the span's start, the span's count of units, or the base's units
  where a layer has no span or the object is a base (`session_unit_outside_object`). A
  re-decode decodes unit `unit − first_unit` of the entry's object. A base's units are read
  in its payload's own format — of its manifest capability's and its fallback's pinned
  formats, the one whose magic the payload starts with — whichever capability the entry
  names. `claim_verify.py` reads
  the units from the integer payloads as `wai-rs` does, and `differential.py` adds random
  sessions holding a base's and its layers' envelopes, as `WAI1` envelopes and as `WAI2`
  components.
- **A layer is one a sink can apply, at its base's first unit** (§2.4.12). An entry naming
  a held layer a sink refuses is refused (`session_layer_not_applicable`): not a
  refinement or with a fallback, its `refines` malformed or absent, a `WAI2` refinement
  component that does not conform (SPEC §7.1), its base a layer, not admitted by the
  layer's capability or decoded only through its fallback, its span or runs outside a
  held base's units, or its payload not `WIR1` with a unit for each it covers
  (staged-delivery §4.1 steps 1–4 and 6). A `WAI2` component and its exploded envelope
  are one base in either form (staged-delivery §9). A held layer's entry states the
  `first_unit` of an entry it is applied over — naming its base through its own
  capability — and an entry naming a `stage` claim's base or a layer it lists states the
  unit `stage_sets` names that claim at (`session_first_unit_mismatch`).
- **An entry names what a sink dispatches** (§2.4.12 rule 4): never a `WAI2` companion,
  a rendition or refinement whose payload does not match its `sha256`, a capability that
  is not dispatchable, or the fallback of a `WAI1` whose capability is companion-only
  (`session_source_mismatch`).
- **One verdict for either name, by capability, and for a key holder** (§2.4.12). An entry
  naming a `WAI2` rendition by its exploded envelope's content hash, with only the `WAI2`
  held, is read as naming the rendition by rules 4–8; the exploded envelope's own bytes,
  held, are a separate delivery. A manifest or rendition is a layer only when its
  capability is refinement-only: a base carrying a stray `refines` is a base, as
  dispatch and selection already treat it (SPEC §4, staged-delivery §3), and `wai stage
  apply` now routes by capability. An encrypted envelope an entry names is accepted —
  a sink holding its key decrypts and dispatches it (SPEC §6.4 step 3) — and what the
  entry states of it is the signer's statement.
- **A WAI2 offers its refinements only when every component conforms** (SPEC §7.1,
  *Selection*). The text now states the reference sink's rule: an envelope one of whose
  components breaks a rule of §7.1 or §5 (`check_component_roles`, now on `ManifestV2`)
  offers none of its refinements, and may still present the rendition it picked. `wai
  stage explode` follows it, refusing to explode a component of such an envelope that is
  of a refinement-only capability or marked a refinement; rule 5 matches it
  for the whole envelope; and http-delivery §7 step 4 states it for a client planning
  from a verified head: each refinement of a nonconforming envelope is dropped
  (`components_nonconforming`), never requested, one stating no `sha256` dropped for that
  too, on the range path and the whole-object path alike. A refinement is one whose
  `refines.base`, read on its own, is the pick's id (`container::refinements_naming`), so
  one whose `refines` is otherwise malformed is reported too; of a conforming envelope
  that is exactly the refinements whose `refines` is well formed. The HTTP corpus adds
  ONC's conforming twin, OCF, whose refinement is fetched and delivered on both paths,
  and ONM and ONX, a `refines` of the pick's id alone (reported) and of no entry's (not
  reported); `OR`'s refinement, which names the pick, is now reported. `falsify/` adds
  `conforming-withheld` and `refines-read-whole`, each with its `http_verify.py
  --mutate`.
- **An encrypted envelope's clear parts are checked** (§2.4.12). SPEC §6.4 encrypts
  payloads only: rules 4–9 now read an encrypted envelope's manifest and rendition table
  as any held envelope's — the rendition an entry names, its capability, the component
  rules, a base's layer count, a companion-only capability, a fallback, and the classical
  length its table declares — and leave to the signer only what needs a payload: whether
  a payload matches its `sha256`, a base's units and a layer's `WIR1` payload. A `sha256`
  that is not 64 lowercase hex digits matches no payload, so an entry naming its rendition
  is refused, encrypted or not (rule 4, `session_source_mismatch`).
- **The recorder states the classical length.** `SessionRecorder` derives
  `classical_declared` at the tally from the `WAI2` envelopes it holds, readable or
  encrypted, exactly as rule 9 counts them (`claim_receipt::session::classical_length`,
  which rule 9 calls too); `SessionRecorder::classical`, which a sink called by hand and
  the browser binding never could, is removed. A page that receives a `WAI2` it does not
  decode now seals its session. `verify_measure` seals two such sessions in wasm32 — over
  `counting/wai2-classical.wai`, whose second rendition is PNG, and its encrypted twin,
  both new in the counting set — checks the length against the rendition table, and the
  native and Python verifiers give the same verdicts. A limitation of this revision: the
  length comes from the `WAI2` envelopes held in full, so a sink that received a `WAI2`
  only as byte ranges, holding its head but not the envelope, cannot yet state that
  envelope's classical rendition (§2.4.12, `classical_declared` and rule 9). Recording
  such a session is HTTP session work still to come.
- **A record or a derivation is no component in any role** (SPEC §7.1, *Component
  identity*; `record-or-derivation-as-component`).
- **A record or a derivation is no companion** (SPEC §7.1, rule 1). A companion's
  capability is companion-only or unregistered; a capability registered in another
  kind — a payload, a record or a derivation — is refused
  (`standalone-as-companion`), which the reference now reads from the registry rather
  than from its list of media capabilities, which left records and derivations out.
- **Every refusal accounted for.** The claim-conformance README's table lists each
  refusal a sink makes that decides whether a presentation can happen — staged-delivery
  §3, §4.1, §4.3–§4.5, §9; SPEC §4, §5, §7.1 — with the §2.4.12 rule and vector that
  refuse it, or the sentence of §2.4.12 that leaves it to the signer's statement.
- **The browser claim is a release build.** `verify_measure_wasm.mjs` requires the wasm32
  claim's `host.build.profile` to be `release` and its target `wasm32-unknown-unknown`.
- **The build profile is the optimisation level.** `release` is a build at `opt-level`
  2, 3, `s` or `z`, whatever its profile is named, and `debug` one at 0 or 1
  ([jwp-receipts](extensions/jwp-receipts.md) §2.4.8's `build`): a WebAssembly build
  optimised for size is `release`.

