fix(client/abr): the decode-cap latch fires on the knee's real presentations #36

Merged
enricobuehler merged 1 commits from worktree-abr-decode-cap-latch into main 2026-08-04 17:46:30 +00:00
Owner

The client-decoder knee latch (decode_cap_kbps) was unreachable in production — zero decode cap learned lines in every field log, while its own doc comment describes the exact sawtooth it exists to end. The 2026-08-03 field trace (1440p120, 780M client decoding at ~4 ms flat): a 9½-minute Automatic session climbed 30 → 452 Mbps, choked at the decoder knee five times, latched nothing, and sawtoothed 220↔450 Mbps until the user relaunched the game (new session = back to the 20 Mbps default — their "workaround").

Three gaps, one shared root:

  1. The ordinary two-bad-window backoff carried no decode evidence. The knee's most common presentation is a standing 15–45 ms decode rise — below the severe tier — across two windows. Evidence was judged from the deciding window alone, so the backoff the decode signal itself caused reset the knee streak. Now the streak carries its own attribution (streak_decode_windows): a backoff whose bad windows were all decode-flagged is decode evidence.

  2. A cascade's second backoff can never agree with the first. A live host acks the ×0.7 request in ~100 ms, so the second sample always sits at the reduced rate — outside the ±1/8 similarity band by construction (0.7 < 7/8). The canonical test never acked between its backoffs, which is how the premise survived. Now a backoff only samples a rate the controller climbed back to (climb_since_backoff, armed by any ack that raises the rate); a drain-time backoff neither latches nor erases the reference the real knee set.

  3. Keyframe storms weren't evidence. The Steam Deck presentation of the knee: the overdriven decoder wedges and begs for keyframes with loss_ppm=0 (14–19 asks at ~300 Mbps in the field traces) rather than showing a clean latency rise. A storm on a clean link is now decode evidence; with real loss present the asks stay network-attributed.

Client-side only — no wire, no ABI, no host changes. The tests were reworked to model the ack round-trip (choke → ack → re-climb → choke) via shared helpers, plus five new ones including a regression test replaying the field trace's real rates and decode figures, which must latch at the second knee encounter — where the live session failed to.

Verified: 288 punktfunk-core lib tests green (42 abr), clippy --all-targets -D warnings clean, fmt clean, all with --features quic (a plain cargo check silently skips the abr module). Still owed: an on-glass Automatic session that climbs into a decoder knee, expecting the decode cap learned log line — can share the run already owed for the #28 sweep.

The client-decoder knee latch (`decode_cap_kbps`) was unreachable in production — zero `decode cap learned` lines in every field log, while its own doc comment describes the exact sawtooth it exists to end. The 2026-08-03 field trace (1440p120, 780M client decoding at ~4 ms flat): a 9½-minute Automatic session climbed 30 → 452 Mbps, choked at the decoder knee five times, latched nothing, and sawtoothed 220↔450 Mbps until the user relaunched the game (new session = back to the 20 Mbps default — their "workaround"). Three gaps, one shared root: 1. **The ordinary two-bad-window backoff carried no decode evidence.** The knee's most common presentation is a standing 15–45 ms decode rise — below the severe tier — across two windows. Evidence was judged from the deciding window alone, so the backoff the decode signal itself caused *reset* the knee streak. Now the streak carries its own attribution (`streak_decode_windows`): a backoff whose bad windows were all decode-flagged is decode evidence. 2. **A cascade's second backoff can never agree with the first.** A live host acks the ×0.7 request in ~100 ms, so the second sample always sits at the reduced rate — outside the ±1/8 similarity band by construction (0.7 < 7/8). The canonical test never acked between its backoffs, which is how the premise survived. Now a backoff only samples a rate the controller climbed back to (`climb_since_backoff`, armed by any ack that raises the rate); a drain-time backoff neither latches nor erases the reference the real knee set. 3. **Keyframe storms weren't evidence.** The Steam Deck presentation of the knee: the overdriven decoder wedges and begs for keyframes with `loss_ppm=0` (14–19 asks at ~300 Mbps in the field traces) rather than showing a clean latency rise. A storm on a clean link is now decode evidence; with real loss present the asks stay network-attributed. Client-side only — no wire, no ABI, no host changes. The tests were reworked to model the ack round-trip (choke → ack → re-climb → choke) via shared helpers, plus five new ones including a regression test replaying the field trace's real rates and decode figures, which must latch at the second knee encounter — where the live session failed to. Verified: 288 punktfunk-core lib tests green (42 abr), clippy `--all-targets -D warnings` clean, fmt clean, all with `--features quic` (a plain `cargo check` silently skips the `abr` module). Still owed: an on-glass Automatic session that climbs into a decoder knee, expecting the `decode cap learned` log line — can share the run already owed for the #28 sweep.
enricobuehler added 1 commit 2026-08-04 16:57:14 +00:00
fix(client/abr): the decode-cap latch fires on the knee's real presentations
ci / rust-arm64 (pull_request) Successful in 1m55s
android / android (pull_request) Successful in 3m32s
ci / docs-site (pull_request) Successful in 1m37s
apple / swift (pull_request) Successful in 1m21s
apple / screenshots (pull_request) Skipped
ci / web (pull_request) Successful in 1m10s
windows / build (x86_64-pc-windows-msvc) (pull_request) Successful in 4m26s
ci / rust (pull_request) Successful in 10m22s
windows / build (aarch64-pc-windows-msvc) (pull_request) Successful in 1m4s
5e19a4611f
The client-decoder knee latch (decode_cap_kbps) was unreachable in
production — zero "decode cap learned" lines across every field log, while
its own doc named the exact sawtooth it exists to end (the 2026-08-03
1440p120 field trace: 220↔450 Mbps for nine minutes, five knee backoffs,
no latch):

- The ordinary two-bad-window backoff — the knee's most common
  presentation, a standing 15–45 ms decode rise below the severe tier —
  carried no decode evidence at decision time, because evidence was judged
  from the deciding window alone. Worse, the backoff the decode signal
  itself caused then RESET the knee streak. Now the streak carries its own
  attribution (streak_decode_windows): a backoff whose bad windows were
  all decode-flagged is decode evidence.

- A cascade's second backoff can never agree with the first: a live host
  acks the ×0.7 request in ~100 ms, so the second sample always sits at
  the reduced rate — outside the ±1/8 similarity band by construction
  (0.7 < 7/8). The canonical test never acked between its backoffs, which
  is how the premise survived. Now a backoff only samples a rate the
  controller climbed back to (climb_since_backoff, armed by any ack that
  raises the rate); a drain-time backoff neither latches nor erases the
  reference the real knee set.

- A keyframe-ask storm on a clean link (the Steam Deck presentation: the
  overdriven decoder wedges and begs instead of queueing — 14–19 asks at
  ~300 Mbps with loss_ppm=0 in the field traces) is decode evidence too;
  with real loss present the asks stay network-attributed.

The reworked tests model the ack round-trip (choke → ack → re-climb →
choke), including a regression test replaying the field trace's rates and
decode figures, which must latch at its second knee encounter.
enricobuehler merged commit 5e319f3b77 into main 2026-08-04 17:46:30 +00:00
enricobuehler deleted branch worktree-abr-decode-cap-latch 2026-08-04 17:46:35 +00:00
Sign in to join this conversation.
No Reviewers
No labels
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: unom/punktfunk#36