wai: the signed session claim — bound to its stage claims, its objects and its decoders #173

Merged
dcharlot merged 28 commits from wai/w5-3-session-claim into main 2026-10-06 13:51:24 -04:00
Owner

The signed session claim: one playback session's account, signed by the sink and bound to the stage claims and objects it names.

What it covers. The session claim class (jwp-receipts §2.4.12) states:

  • what was presented when;
  • the bytes transferred and counted;
  • what was missed and switched, within exact caps;
  • the decoders each output came from;
  • an energy figure, only with its acquisition class.

What a verifier checks when it holds the objects and claims named:

  • Stage claims, both ways. Each unit's presentations are bounded by the applied and partial layers of the stage claims named for it, one per epoch, so a session that outlives a forgotten base still seals.
  • Objects received. The subject, every layer object a stage claim says arrived, and every decoder's source are among the objects received.
  • Decoders. A decoder entry names the object it decoded. Its capability must be what that object's envelope gives, so a signer cannot relabel a decoder out of the re-decode check.
  • Classical renditions. A classical rendition is a classical capability of the envelope's media, drawn from the codec glossary. An encrypted envelope is refused.

What stays the signer's statement. Timings, missed deadlines, switches, energy, and which object an output came from remain the signer's statements, and the text says so.

Authorities. The figure is a second confirmation item, so a key without the energy authority confirms a session but never its figure (§7.4, §7.5). Delivery telemetry's claim member now names this registered class.

The recorder. session_recorder tallies a session, verifies the stage claims it names, and checks its own seal before releasing it. Every staged-conformance case replays through the sink, the observer and the recorder into a session that seals.

Measurement. wai_stage_measure bench --sign times each base decode and each layer's offer per (unit, stage), with every run's outputs compared, and records each run.

  • A metered session runs its burst on a power-of-two worker count, so the host's thread count is rounded for privacy and the figure stays exactly recomputable.
  • Counter readings end at an implausible power, interval or delta.
  • rail_liveness refuses a frozen rail.

Corpora.

  • The claim corpus is appended, with the pre-branch vectors pinned by digest, and the signer corpus gains the figure authority.
  • Staged measurement's output tree now commits to each decoder's source object; its goldens are regenerated through their generator.
  • claim_verify.py, signer_verify.py and differential.py agree with wai-rs, including on adversarial multi-rendition manifests.

Checked locally on macOS arm64:

  • the link gate;
  • both corpora regenerated byte-identically through both implementations;
  • the differential at three seeds;
  • the replay over every staged case;
  • the public-text and section-reference checks.

The forge CI runs on this pull request.

🤖 Generated with Claude Code

The signed session claim: one playback session's account, signed by the sink and bound to the stage claims and objects it names. **What it covers.** The `session` claim class (jwp-receipts §2.4.12) states: - what was presented when; - the bytes transferred and counted; - what was missed and switched, within exact caps; - the decoders each output came from; - an energy figure, only with its acquisition class. **What a verifier checks** when it holds the objects and claims named: - **Stage claims, both ways.** Each unit's presentations are bounded by the applied and partial layers of the stage claims named for it, one per epoch, so a session that outlives a forgotten base still seals. - **Objects received.** The subject, every layer object a stage claim says arrived, and every decoder's source are among the objects received. - **Decoders.** A decoder entry names the object it decoded. Its capability must be what that object's envelope gives, so a signer cannot relabel a decoder out of the re-decode check. - **Classical renditions.** A classical rendition is a classical capability of the envelope's media, drawn from the codec glossary. An encrypted envelope is refused. **What stays the signer's statement.** Timings, missed deadlines, switches, energy, and which object an output came from remain the signer's statements, and the text says so. **Authorities.** The figure is a second confirmation item, so a key without the energy authority confirms a session but never its figure (§7.4, §7.5). Delivery telemetry's `claim` member now names this registered class. **The recorder.** `session_recorder` tallies a session, verifies the stage claims it names, and checks its own seal before releasing it. Every staged-conformance case replays through the sink, the observer and the recorder into a session that seals. **Measurement.** `wai_stage_measure bench --sign` times each base decode and each layer's offer per (unit, stage), with every run's outputs compared, and records each run. - A metered session runs its burst on a power-of-two worker count, so the host's thread count is rounded for privacy and the figure stays exactly recomputable. - Counter readings end at an implausible power, interval or delta. - `rail_liveness` refuses a frozen rail. **Corpora.** - The claim corpus is appended, with the pre-branch vectors pinned by digest, and the signer corpus gains the figure authority. - Staged measurement's output tree now commits to each decoder's source object; its goldens are regenerated through their generator. - `claim_verify.py`, `signer_verify.py` and `differential.py` agree with wai-rs, including on adversarial multi-rendition manifests. **Checked locally on macOS arm64:** - the link gate; - both corpora regenerated byte-identically through both implementations; - the differential at three seeds; - the replay over every staged case; - the public-text and section-reference checks. The forge CI runs on this pull request. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
jwp-receipts §2.4.12 registers reconstruct-session, the next free number after
W1-5's stage (§2.4.10) and delivery-gap (§2.4.11): a sink's statement of one
session — decoders and host, paths and switches, late presentations, the output
tree, counted and transferred bytes, the classical rendition it did not need,
the stage claims it signed, and its decode energy where metered. The session
names its sets' stage claims rather than restating their objects and units;
step 6 checks it against its outputs, objects, entry envelopes and those stage
claims. §3.1 gains seventeen reasons, §5.11 the corpus, §7.5 the authority
and the energy figure as a second item. energy-measurement §6.1 widens the
claim-link profile; delivery-telemetry's claim member now names a registered
class; staged-measurement §8.6-§8.7 and Appendix C say how a tool checks the
rail and seals a session's figure.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
claim_receipt::session reads and checks a session body (jwp-receipts §2.4.12):
step 2 in the member table's order, the twelve step-4 rules, and the step-6
checks against the presentations (the output tree, first-use order, paths,
missed presentations, re-decodes through byte-exact decoders), the objects
received, the entry envelopes' classical rendition and the stage claims the
session names. A session's metered figure is viewed under moq-jwp:claim-link,
and figure_authorities() gives its second confirmation item. The output tree
builds under receipt_claims too; receipt_claims takes telemetry (no
dependency) for delivery-telemetry §5's switch reasons.

claim-conformance gains the session vectors, appended: the earlier vectors
are an unchanged prefix, pinned by digest like the two prefixes before.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
claim_verify.py checks a session as jwp-receipts §2.4.12 states it, written
from the text: step 2 in the table's order, the twelve rules, an independent
output tree (SHA-256, RFC 6962), first-use order, the counted sums, a WAI2
reading of the entry envelope with the registry's replicate classes, and the
stage claims a session names. Every vector reports its declared outcome.
differential.py builds sessions from random presentation lists, as an honest
sink states them, gives them a defect or two, and requires both verifiers to
agree; the README describes the vectors and the family.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
key_log::Item::figure_of_claim gives the energy figure of a metered
reconstruct-session claim as a second item (jwp-receipts §7.5): it needs
reconstruct-session and energy/<class>, every one of them, as confirm already
requires of an item's authorities. A verifier reports the session's
confirmation and its figure's side by side. signer-conformance gains the
cases: a key holding reconstruct-session but not energy/OnChipCounter
confirms the session and has its figure refused; a key holding both, under a
fetched anchor, leaves the figure unconfirmed; under a pinned one, both are
confirmed; a did:key leaves both unconfirmed. signer_verify.py gives every
verdict; the earlier vectors are an unchanged prefix.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
A SessionRecorder is told the decoders, objects, presentations (with how late),
switches, stage claims, classical rendition and decode figure of one session,
and tallies them as jwp-receipts §2.4.12 states them: entries in first-use
order with unused decoders left out, leaves, units and paths, the output tree,
the first 1024 switches and 256 missed presentations in order, and the counted
sums. seal_checked seals (§3.1 steps 1-4) and checks the claim against the
presentations and objects (step 6) before it returns, so a mis-tallied body
never leaves the sink.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
energy_report::rail_liveness classifies a probe of power samples read with
nothing running as absent (no sample), stale or live, by energy-measurement
§3.1's own rule, so a tool brackets nothing over a rail that is frozen, held or
refreshed in batches (staged-measurement §8.6). energy_burst::
counter_power_samples turns a microjoule counter into power samples: each
difference over its measured interval, across the counter's wrap, a failed
read ending the samples; rapl_power_reader wires it to the powercap counter for
measure_with.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
stage_measure::run::completion decodes every base and applies every layer of a
title: one completion, the work a decode time or figure is stated per.
stage_measure::session builds the session a sink decoding a title records —
decoder entries per (capability, pin) in first-use order, the pin's declared
tier, every presentation and object — and seal_session seals it with a
published figure and, beside one, the measurement claim on it, chained after
the session under the same key; the session's tree is checked to be the
title's. A lib test seals every fixture's session over its evaluation tree,
and a metered session with its report.

wai_stage_measure bench times completions, probes the meter's rail at rest
(rail_liveness) before bracketing (macmon, or rapl:<domain> through
counter_power_samples), publishes only a clean figure, and with --sign seals
the session and its report; --record writes the machine, build, load, timings,
energy facts and claims. Never in CI.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
Co-Authored-By: Claude Opus 5.5 <[email protected]>
The corpus gains sessions of 300 units at and one past the caps of 1024 switches
and 256 missed presentations written; differential.py adds the same around the
caps, where no earlier rule decides, so a verifier that is off by one at either
cap disagrees.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
Co-Authored-By: Claude Opus 5.5 <[email protected]>
delivery_terms holds delivery-telemetry §5's switch reasons, which a session
claim and the session recorder read and telemetry::observation re-exports, so
receipt_claims builds without the telemetry module and wai_telemetry stays on its
own feature. energy_report's flat-bracket test has its doc comment back.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
counter_power_samples read any decrease as a wrap, so a counter reset to a low
value read as nearly its whole range in one interval: megawatts. An interval
whose difference implies more than 5 kW now ends the samples, as a failed
read does, and so does a counter that moves while the clock stands still. The
review's probe E1 is a test; staged-measurement §8.6 states the bound.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
The review found a session that could contradict what it names. Fixed in the
text, wai-rs and claim_verify.py together:

- Stage claims (§2.4.12 step 6): with the objects held, a stage claim's base
  and every layer object it lists as arrived must be among the objects
  received; with the outputs held, every unit presented at more than s layers
  must lie in the span of a layer-s object that arrived (applied, partial, late
  or refused), as well as the earlier bound from below.
- The entry object must be among the objects received
  (session_entry_not_received).
- Envelopes: the context is every WAI2 envelope received, by bytes. Each must be
  plaintext (an encrypted one is inert, SPEC §6.4). A decoder entry naming a
  rendition must name that rendition's capability (session_rendition_mismatch),
  so a signer cannot relabel a byte-exact decoder out of the re-decode check. The
  classical rendition is read by a stated rule — the first selectable rendition
  of a classical capability (the codec glossary's era, codecs::is_classical, held
  to the glossary by a test) of the envelope's media, not text — never named.
- Rules 7 and 8: exactly min(switches_total, 1024) switches and min(missed, 256)
  missed presentations are written.
- The model stage set's base is now a received object and the session's entry
  object; the corpus is regenerated (447 vectors) with the review's probes as
  vectors: S1, S2, S3, S4, the relabel, the caps and representative classical
  renditions. The vectors before the class are unchanged, pinned by digest.
- §2.4.12's closing paragraph now says what is reproducible and where a
  decoder's capability is bound.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
The random session family gives its sessions an entry object received or not,
stage claims whose base and layers are among the objects or not, and envelopes
held once, twice or not at all. New families: stage bounds (sessions passing
steps 1-5 against a stage claim whose first layer applied to some units and
arrived late for others, presentations at one to three layers, objects with and
without its base and layers); the caps of switches and missed presentations
written, around both caps; the review's adversarial WAI2 manifests and envelope
framings; and every registered capability as an envelope's second rendition in
two media. Both verifiers must agree on each.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
Co-Authored-By: Claude Opus 5.5 <[email protected]>
The recorder keeps the wire form of each stage claim it names, refuses one that
does not verify or is another key's than those before it, and seal_checked runs
check_session_stage_claims against the presentations and objects it recorded.
A session finished with another key than its stage claims' (context_mismatch),
or presenting fewer units than a stage claim counts (session_stage_disagrees),
never leaves the sink: the review's R1 and R2 make finish return Err.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
bench sealed a session with threads 1 beside a figure bracketed on up to ten
workers. stage_measure::session::host_for writes the bracket's in_flight as
SessionHost::rounded_threads wherever a bracket is attached; seal_session
refuses a host that misstates it, and check_host_agrees checks a session against
its paired measurement claim. The lib test seals with the host the figure gives
and checks the two agree.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
Co-Authored-By: Claude Opus 5.5 <[email protected]>
staged-measurement §7.1-§7.4 as written: stage_measure::decode::{steps, prepare}
make each decode step of a title — a base decoded, one layer applied — ready on
its own, every step before it done untimed, and Prepared::run gives its (unit,
layers) outputs. bench times every case each round, the order rotated by one
round to round, warm-up rounds discarded; every run's outputs are compared with
the evaluation's (differs_from) and a difference aborts the bench with no
timing kept. The record carries each case's runs and quantiles, the order the
cases ran in, the load before and after each block, and the operator's --idle
statement. Tests: every step of every fixture, run alone and out of order, gives
the evaluation's outputs; a run that differs is found.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
The CHANGELOG's session entry states the both-way stage binding, the entry
object rule, the rendition binding, the classical rule by the glossary, the
exact caps, the recorder's stage-claim checks, bench's per-case timing and the
counter bound, and that receipt_claims no longer takes telemetry.
staged-measurement §8.7 and jwp-receipts §2.4.12 state that a metered session's
threads are its figure's bracket's in_flight.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
#172 regenerated wai.h without CHAIN_REQUESTED_LINKS, so this branch's header
matches main's: git diff origin/main -- wai/wai-rs/include/wai.h is empty.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
A counter reset across a long interval implied less than 5 kW and was read as
a sample: a powercap package range over a minute is about 4.4 kW.
counter_power_samples takes the sampling interval and also ends the samples at
an interval longer than ten of them, and at a difference of more than half the
counter's range. staged-measurement §8.6 states both. The test
a_reset_across_a_long_interval_ends_the_samples holds the review's reset over a
minute, and each bound on its own at its edge.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
A timed base case ran decode_base, which compares the decode with the case's
stage 0 and checks its case after decoding, and both cases hashed every output
inside the clock. Prepared::run now does the step's own work only — the pinned
base decode, or the sink's offer of the layer — and gives a Ran, whose outputs()
checks a base against its case and hashes the outputs after the clock stops.
The record's work string names what is untimed: the case's preparation, which
applies every earlier layer (it said later), the stage-0 comparison and the
hashing. The README states the same.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
The measurement claim beside a metered session published the bracket's exact
in_flight (ten) next to a session that rounds its workers to eight, so the pair
named more of the machine than jwp-receipts §6.7 says a session does. Rounding
in_flight after the fact would change the figure, whose work (2c + n)/2 counts
it, so the burst itself runs on a power of two of workers:
energy_burst::power_of_two_workers and measure_on, which bench uses.
seal_session refuses a bracket on any other count, and check_host_agrees
requires the report's in_flight to be the session's threads. The test seals on
eight, refuses ten under either host, and holds a hand-sealed session of eight
beside a report of ten as disagreeing.

jwp-receipts §2.4.12 and §6.7 and staged-measurement §8.7 state the rounding.
§2.4.12 states a limit: nothing binds a stage claim to a session beyond its
base and key, so one from an earlier session of the title by the same key can
be named.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
seal_session's own check_host_agrees also refuses it, after sealing; the test
now requires the power-of-two refusal, which comes before anything is sealed.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
N1: an honest session whose sink forgot its base was refused. A sink signs a
stage claim for each epoch of a set and names them all; the bound from above
now takes, for each unit of a set presented at more than s layers, every
claim whose named set holds the unit, and requires a layer-s object one of
them lists as applied or partial. The replay of staged-conformance showed
that grouping by base alone refuses record_budget, whose three bases cover
the same units, so the union is per unit of the title, not per base.

N2: late and refused objects no longer bound anything: neither applied to a
unit (§2.4.10). "Arrived" still decides membership in the objects received.

The recorder's stage_set says a sink names each epoch's claim. New lib tests
in staged/claim_tests.rs: every_corpus_case_seals_an_honest_session replays
every staged-conformance case, signs the claim of every epoch from the chain
held before each forget and at the end, and requires the recorder to seal;
the_forget_vectors_are_what_the_sink_states pins the corpus's epoch claims to
the observer's, and seals with both named and refuses with only the last.

Vectors: session_forget_accept, session_forget_first_epoch_accept,
session_forget_last_epoch_only, session_forget_beyond_both_epochs (from
record_budget's envelopes, included by the generator); U1, U2 and U4 as
session_stage_through_late_layer1, _through_late_layer2 and
_through_refused_layer (the late-layer accept vector is now U2's refusal);
S1 renamed session_stage_beyond_applied. claim_verify.py follows;
differential.py gains session_stage_epochs. jwp-receipts §2.4.12 checks 4-5,
the stage_sets row, the reason row and §5.11 state it.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
N3: the re-decode escape was closed for one route only. Each decoder entry now
has an `object`, the content hash of the envelope it decoded: the WAI2 envelope
whose rendition it is, or the WAI1 envelope, a base or a layer. With the
objects, each entry's object is among them (session_source_not_received). The
envelopes context holds every WAI2 envelope received and any WAI1 envelope the
verifier holds: each must be among the objects received
(session_source_not_received), read as a plaintext WAI1 or WAI2 envelope
(session_envelope_unreadable), and an entry whose object is one of them must
name what it gives — a rendition and its capability, or no rendition and the
WAI1 manifest's capability or fallback; an entry naming a rendition must have
its envelope held (session_source_mismatch, which replaces
session_rendition_mismatch). Routes Ra-Re of the review are vectors:
session_relabel_wai1_base, _av1 and _j2k, _rendition_nulled, _other_envelope,
_envelope_not_received.

An entry decodes one object, so the model's refinement is an entry per layer
object, and a title's output-tree entries are (capability, pin, object):
staged-measurement Appendix C, stage_measure::run and measure_verify.py follow,
and goldens.json is regenerated (roots and decoder indices change; quality and
sim results do not). A fixture's session still commits to its title's tree.

The model's base is a real WAI1 envelope. The recorder's DecoderKey carries the
object. claim_verify.py follows; differential.py signs each envelope variant's
session again so its entry names that envelope, adds a WAI1 manifest family,
and gives random sessions sources and envelope contexts. jwp-receipts §2.4.12
(the object row, checks against objects and envelopes, the closing paragraph,
which states that the object an entry names is the signer's statement), the
reason table and §5.11 state it; the signer corpus's session gains the member.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
wai: CHANGELOG for the session's third round
Some checks failed
standards workspaces build + test / macOS energy backends (pull_request) Failing after 6m4s
standards workspaces build + test / sandbox-rs (pull_request) Successful in 2m15s
standards workspaces build + test / sense-rs (pull_request) Successful in 1m4s
WAI conformance / cargo-class corpora · linux-arm64 (pull_request) Successful in 7m44s
WAI conformance / cross-arch byte-equality · linux-arm64 (pull_request) Successful in 40s
standards workspaces build + test / smart-byte-rs (pull_request) Successful in 3m37s
WAI energy receipts / energy receipts · linux-arm64 (pull_request) Successful in 2m49s
wai-pack / wai-pack · linux-arm64 (pull_request) Successful in 1m52s
WAI conformance / cargo-class corpora · linux (pull_request) Successful in 5m49s
WAI conformance / v2 vocabulary · manifests validate as stated (pull_request) Successful in 12s
WAI conformance / independent verifiers · claims, JWP, key discovery and integer payloads (Python) (pull_request) Successful in 16m34s
WAI conformance / delivery telemetry · CMCD and CMSD against an independent library (pull_request) Successful in 7s
WAI conformance / cross-arch byte-equality · linux (pull_request) Successful in 23s
WAI conformance / C2PA manifest emission · signed, read back, validated (pull_request) Successful in 44s
WAI conformance / cargo-class corpora · macos (pull_request) Successful in 30m13s
WAI energy receipts / energy receipts · linux (pull_request) Successful in 1m37s
WAI conformance / cross-arch byte-equality · macos (pull_request) Successful in 34s
WAI energy receipts / energy receipts · macos (pull_request) Successful in 3m44s
WAI HTTP delivery / HTTP corpus · falsification and differential (pull_request) Successful in 13m6s
WAI HTTP delivery / cache rules · controls and labelled caches (pull_request) Successful in 56s
wai-pack / wai-pack · macos (pull_request) Successful in 10m49s
WAI JPEG-AI whole decode (pinned inputs, required) / fetch pinned JPEG-AI inputs → whole decode + pixel hash, required (pull_request) Successful in 1m4s
WAI neural sink gates / model-free refusals of the neural sink (pull_request) Successful in 1m7s
wai-pack / wai-pack · linux (pull_request) Successful in 1m1s
wai-pack / wai-pack · independent verifier (Python) (pull_request) Successful in 1m43s
wai-pack / wai-pack · the floor through a media prober (pull_request) Successful in 15s
wai-pack / wai-pack · dependency licences (pull_request) Successful in 1m12s
WAI section references / cited sections exist (pull_request) Successful in 9s
WAI mbt2018_mean integer decode (regenerated vectors) / regenerate mbt2018_mean integer vectors → Rust decode, required (pull_request) Successful in 4m0s
wai-web wasm32 / wai-web wasm32 build · corpus, menu and writers (pull_request) Successful in 1m54s
3efe39c37d
The session entry states the epoch claims and the per-unit bound by applied
layers, the decoder entry's object and the checks against the envelopes, the
stale-claim limit, the counter's stall and half-range bounds, the power-of-two
burst behind a metered session's report, bench's base timing, the replay test,
and that a title's output root under Appendix C changes as its entries name
their envelopes.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
Sign in to join this conversation.
No reviewers
No labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
Transaction-Science/open-standards!173
No description provided.