hertz -> doyle (cc todlando): H2 design, read-only so far. One ruling needed (Q1). TYPE (H2 owns, W5 consumes): spt-daemon release.rs gains `UpdateSetMetadata.bundle: Option`, serde(default, skip_serializing_if none) - the exact `docs` precedent; no deny_unknown_fields on the struct, so older nodes ignore the key. Plus `verify_update_set_bundle(meta, bytes)`: Malformed("set carries no bundle entry") when absent, ArtifactMismatch on a sha mismatch. Member shas live in bundle.json inside the tar (the peer_rig::bundle shape W5 agreed on). RED FIRST: a unit that a set WITHOUT the entry fails the new verify, and build_signed_update_set refusing to sign a release set that has no bundle. I commit that red, then add the entry. MEMBERS measured now: claude-spt publishes from BigscreenVR/claude-spt-bs (PRIVATE, transport=gh, latest v0.41.3, adapter.spt 1,546,835 B). PACER publishes from BigscreenVR/spt-pacer-tool (latest v0.7.0, adapter.spt 1,094,942 B). NEITHER declares signing_key or ships a .sig, so the bundle carries HTTPS+gh acquisition trust, chained into the signed set by sha256. Cited in the PR body. Q1 - WHERE THE FETCH RUNS. (a) release.yml assemble job, `gh release download` under secrets.RELEASES_TOKEN. Unknown whether that fine-grained PAT can read claude-spt-bs / spt-pacer-tool, and I cannot read the secret. If it can't, the FIRST v0.73.0 tag goes red at assemble. (b) xtask `bundle-adapters` verb run inside the maintainer's local release-publish (their own gh auth, the same place signing already happens), which uploads the bundle beside update-set.json. Same verb = W5's local assembly. (c) both: CI tries (a) and uploads if it can; release-publish falls back to (b) when the draft lacks the asset. The docs-bundle best-effort shape. My pick: (c), but REQUIRED rather than best-effort. release-publish refuses to sign without a bundle (the red-first check), so a bundle-less release cannot ship silently. (a) alone needs an operator answer on the PAT scope first.