fix(probe): the format was on the wire but the capability bit was not, so the host was right to ignore it

Correcting my own previous commit, which claimed the probe had been "asking for the
lossless plane by accident". It had not, and the comment I deleted for being wrong was
right.

This `Hello` is built BY HAND and never passes through `advertised_client_caps`, the
helper that derives CLIENT_CAP_AUDIO_HIRES from the requested format for the shipping
clients. So the probe's explicit 48000/16 was inert: the host's gate tests the
capability first, and without the bit a format on the wire is a request it correctly
ignores. No session was ever mislabelled, and the old comment's "never sets
CLIENT_CAP_AUDIO_HIRES" was an accurate statement about this file.

What was actually broken is what I added: `--audio-format` set the rate and depth and
not the bit, so it asked for nothing. Found on glass — the host resolved
`plane="0xC9 Opus"` while every gate condition looked satisfiable, and nothing was
logged, because condition 1 is deliberately unlogged (it is every session with every
shipping client). Both are set together now, and the comment says why they must be.

With that fixed the plane resolves end to end against a real Linux host: 0xD3, 96 kHz,
24-bit, 192 samples per channel — the 2 ms frame the ladder predicts for 96/24 at the
default MTU.
This commit is contained in:
2026-08-16 12:37:47 +02:00
parent bca85db160
commit 362be21ebb
+23 -13
View File
@@ -590,24 +590,34 @@ async fn session(args: Args) -> Result<()> {
// it would just strip the pointer from the dumped bitstream. `--cursor-capture`
// advertises it deliberately and then flips the channel to the capture model, so the
// HOST composites and the dump is where the pointer must appear.
client_caps: if args.cursor_capture {
punktfunk_core::quic::CLIENT_CAP_CURSOR
} else {
0
client_caps: {
let mut c = if args.cursor_capture {
punktfunk_core::quic::CLIENT_CAP_CURSOR
} else {
0
};
// ⚠ This `Hello` is built BY HAND, so it never passes through
// `client::advertised_client_caps` — the helper that derives this bit from the
// requested format for the shipping clients. The rate and depth below are
// therefore inert on their own: the host's gate checks the CAPABILITY first, so a
// format without this bit is a request the host is right to ignore. Set both, or
// neither.
if args.audio_format.is_some() {
c |= punktfunk_core::quic::CLIENT_CAP_AUDIO_HIRES;
}
c
},
// Like STREAMED_AU above: the shared-core reassembler pins geometry per-frame, so
// the probe accepts a mid-session shard change (and jumbo growth) up to the
// receive ceiling — and it's exactly the tool to measure both.
max_shard_payload: punktfunk_core::config::max_shard_payload() as u16,
// `0`/`0` is UNSPECIFIED, and that distinction is load-bearing rather than cosmetic:
// `advertised_client_caps` sets CLIENT_CAP_AUDIO_HIRES when EITHER field is non-zero,
// because it keys on "the caller specified a format" — otherwise 48 kHz/16-bit, the
// cheapest lossless rung, would be the one format nobody could request. So the
// explicit `SAMPLE_RATE_HZ`/`BITS_16` this used to send was a genuine hi-res request:
// against a host with the operator policy on, the probe advertised the capability,
// was given the `0xD3` plane, and then silently counted nothing — its decode arm only
// handled `0xC9`. The comment here even claimed it "never sets
// CLIENT_CAP_AUDIO_HIRES", which made the bug invisible to a reader.
// `0`/`0` is UNSPECIFIED. For the shipping clients that distinction decides the
// capability bit — `advertised_client_caps` sets it when either field is non-zero,
// because it keys on "the caller specified a format", so that 48 kHz/16-bit (the
// cheapest lossless rung) is not the one format nobody can request. This `Hello` is
// hand-built and does not use that helper, so here the two are set together above and
// the sentinel is about honesty rather than mechanism: an inert format on the wire
// invites exactly the misreading that a rate alone asks for something.
audio_rate_hz: args.audio_format.map(|(r, _)| r).unwrap_or(0),
audio_bits: args.audio_format.map(|(_, b)| b).unwrap_or(0),
}