0.32.0 — the bitrate becomes the wire budget, and the connect ladder ends #420

Merged
enricobuehler merged 4 commits from worktree-release-0320-prep into main 2026-08-27 20:29:14 +00:00
Owner

Version bump, release notes, CHANGELOG section and Play "What's new" for v0.32.0, cut at db0f4f1e — plus one fix that had to ride along, because it blocks the Windows installer.

90 commits since v0.31.4 (63 non-merge).

🛑 A release blocker found while watching CI, fixed here (87cb4314)

package's "Clippy (host + tray, Windows)" step has been failing on every push since 11217341 landed (#413). The package job dies before "Pack + sign installer", so a tag cut over this main would publish a release with no Windows host attached.

error[E0433]: cannot find `stall` in `super`
    --> crates\pf-capture\src\windows\idd_push.rs:2528:27

mod stall; is declared at idd_push.rs:332, so stall is a child of idd_push and the test module is its sibling. A nested fn does not add a module level for path resolution, which is what the extra super:: was reaching for — the use super::stall::{…} two lines above, in the same test fn, already had the depth right.

It is Windows-only and test-only, so it reached main behind a green Build: the error appears solely under --all-targets, when the lib test target is compiled. Same shape as #408 — a Windows break the Mac and Linux dev loops cannot see. The commit that introduced it verified the logic by extracting stall.rs into a native scratch crate, so this test was never compiled for Windows before merging.

Reproduced and verified from the Mac with scripts/xcheck.sh, in seconds:

Command Before After
scripts/xcheck.sh windows clippy exit 101, error[E0433] at :2528 exit 0
scripts/xcheck.sh linux clippy exit 0

One other red run on main is NOT a code defect

ci.yml run 20723 on db0f4f1e failed at Doc-tests pf_capture with can't find crate for pf_frame — while the same run's Build and Clippy steps compiled pf-capture fine, and the failing rustdoc command has --extern pf_frame=… right there in it. cargo test -p pf-capture --doc passes locally (the crate has no doctests). That is a stale artefact in the shared cache, not a defect — no fix needed, but worth knowing before someone chases it.

The C ABI steps 25 → 26

punktfunk_connect_opts closes the punktfunk_connect_ex* ladder. Eleven generations each added a field by minting a new exported symbol plus a 20-something-parameter forwarding shim; the replacement takes every option in one size-prefixed PunktfunkConnectOpts. Every ex keeps its symbol and its behaviour, so this is an added surface rather than a changed one.

⚠️ This landed while the notes were being written. The first cut of this branch sat on d2286da9 and its version table said "C ABI 25, unchanged". PRs #415–#419 merged in between, and 9c164aa6 carried the step. The branch was rebased onto db0f4f1e and every table row re-derived — tagging the first cut would have shipped a version table that was false.

The minor bump was already right without the ABI step

ABR Phase 4 redefines every control-plane bitrate as the total wire budget rather than the encoder rate, deliberately without a capability handshake. include/punktfunk_core.h already documented the 10-bit SDR ask as "0.32" and said "every pre-0.32 client sets the two bits together", so the tree had pre-decided the number.

Version table

v0.31.4 v0.32.0
Wire protocol 2 2 — three additive, ignorable additions
C ABI 25 26punktfunk_connect_opts
Crate dirs / members 27 / 39 27 / 39
Driver protocol 6 6 — no diff at all
Gamepad channel 3 3
Plugin index schema 1 1
Host event schema 1 1 — no diff at all
api/openapi.json 0.31.4 0.32.0 — content-identical, re-stamped
gamescope patch level pfhdr8 pfhdr8
@punktfunk/host 0.1.6 0.1.6
@punktfunk/plugin-kit 0.4.4 0.4.4 — deliberately

plugin-kit stays put on purpose: plugin-kit/src/wire.ts changes exactly one line, and it is a doc comment in the launch.kind table. launcher_ui is a bare string validated host-side, so nothing to republish.

Seven behavioural breaking changes, none of them a version step

The wire-budget redefinition · FLOOR_KBPS 5000 → 2000 · DSCP defaults to AUTO (local peers only) · GameStream video and control encryption default on — both graduated only after their own on-glass pass on .173, and in both the client opted in unprompted on a LAN · PyroWave forces Automatic bitrate · the console pairing routes move behind the console password · deep links auto-dial by stable record id only.

A CHANGELOG entry was filed under a frozen section

ae13b29a (launcher_ui gains heroic-console, #402) landed after the v0.31.4 tag, but its CHANGELOG block was appended to the v0.31.4 section — so a shipped release's notes described a change it does not contain. Moved into the v0.32.0 section verbatim.

Release notes are written to ISO 24495 plain language

Sentences under 30 words, paragraphs under six sentences, bold bullet lead-ins, an explicit overview, a table of contents worded identically to its headings, and acronyms defined on first use. The plugin's own auditor reports 0 findings on the Play notes and 2 on the release notes — the words NVIDIA and AMD, which are company names rather than acronyms, left unexpanded deliberately.

Gates on the cut (the Mac)

  • cargo fmt --all --check
  • cargo metadata --offline; lock diff versions-only 36/36
  • cargo test -p punktfunk-core --lib --features quic503 passed / 0 failed
  • C ABI harness PASSED (abi_version=26); the regenerated header is not stale
  • scripts/xcheck.sh windows clippy and scripts/xcheck.sh linux clippy — both exit 0
  • scripts/ci/check-docs-drift.sh, scripts/ci/check-docs-links.sh
  • Play notes gate 412/500 chars, unique against 15 files
  • Both openapi.json copies cmp identical, stamped 0.32.0
  • cargo audit green — h2 0.4.18 → 0.4.19 closed RUSTSEC-2026-0258, left open deliberately at the v0.31.1 cut

⚠️ api/openapi.json is re-stamped, not regenerated (punktfunk-host does not build on macOS). It is content-identical to v0.31.4, so there is nothing to regenerate.

After merging

Re-read main's tip before pushing the tag — it moved twice during this prep alone. Then tag, wait for CI green with no run in flight on the merge sha, and dispatch announce with the tag.

Version bump, release notes, CHANGELOG section and Play "What's new" for **v0.32.0**, cut at `db0f4f1e` — plus one fix that had to ride along, because it blocks the Windows installer. 90 commits since v0.31.4 (63 non-merge). ## 🛑 A release blocker found while watching CI, fixed here (`87cb4314`) **`package`'s "Clippy (host + tray, Windows)" step has been failing on every push since `11217341` landed (#413).** The `package` job dies before "Pack + sign installer", so **a tag cut over this main would publish a release with no Windows host attached.** ``` error[E0433]: cannot find `stall` in `super` --> crates\pf-capture\src\windows\idd_push.rs:2528:27 ``` `mod stall;` is declared at `idd_push.rs:332`, so `stall` is a child of `idd_push` and the test module is its sibling. A nested `fn` does not add a module level for path resolution, which is what the extra `super::` was reaching for — the `use super::stall::{…}` two lines above, in the same test fn, already had the depth right. It is **Windows-only and test-only**, so it reached main behind a green `Build`: the error appears solely under `--all-targets`, when the lib **test** target is compiled. Same shape as #408 — a Windows break the Mac and Linux dev loops cannot see. The commit that introduced it verified the logic by extracting `stall.rs` into a native scratch crate, so this test was never compiled for Windows before merging. Reproduced and verified from the Mac with `scripts/xcheck.sh`, in seconds: | Command | Before | After | |---|---|---| | `scripts/xcheck.sh windows clippy` | exit **101**, `error[E0433]` at `:2528` | exit **0** | | `scripts/xcheck.sh linux clippy` | — | exit **0** | ### One other red run on main is NOT a code defect `ci.yml` run 20723 on `db0f4f1e` failed at `Doc-tests pf_capture` with `can't find crate for pf_frame` — while the same run's `Build` and `Clippy` steps compiled `pf-capture` fine, and the failing rustdoc command has `--extern pf_frame=…` right there in it. `cargo test -p pf-capture --doc` passes locally (the crate has no doctests). That is a stale artefact in the shared cache, not a defect — no fix needed, but worth knowing before someone chases it. ## The C ABI steps 25 → 26 `punktfunk_connect_opts` closes the `punktfunk_connect_ex*` ladder. Eleven generations each added a field by minting a new exported symbol plus a 20-something-parameter forwarding shim; the replacement takes every option in one size-prefixed `PunktfunkConnectOpts`. **Every `ex` keeps its symbol and its behaviour**, so this is an added surface rather than a changed one. ⚠️ **This landed while the notes were being written.** The first cut of this branch sat on `d2286da9` and its version table said "C ABI 25, unchanged". PRs #415–#419 merged in between, and `9c164aa6` carried the step. The branch was rebased onto `db0f4f1e` and **every table row re-derived** — tagging the first cut would have shipped a version table that was false. ## The minor bump was already right without the ABI step ABR Phase 4 redefines every control-plane bitrate as the **total wire budget** rather than the encoder rate, deliberately without a capability handshake. `include/punktfunk_core.h` already documented the 10-bit SDR ask as "0.32" and said "every pre-0.32 client sets the two bits together", so the tree had pre-decided the number. ## Version table | | v0.31.4 | v0.32.0 | |---|---|---| | Wire protocol | 2 | **2** — three additive, ignorable additions | | C ABI | 25 | **26** — `punktfunk_connect_opts` | | Crate dirs / members | 27 / 39 | **27 / 39** | | Driver protocol | 6 | **6** — no diff at all | | Gamepad channel | 3 | **3** | | Plugin index schema | 1 | **1** | | Host event schema | 1 | **1** — no diff at all | | `api/openapi.json` | 0.31.4 | **0.32.0** — content-identical, re-stamped | | gamescope patch level | pfhdr8 | **pfhdr8** | | `@punktfunk/host` | 0.1.6 | **0.1.6** | | `@punktfunk/plugin-kit` | 0.4.4 | **0.4.4** — deliberately | `plugin-kit` stays put on purpose: `plugin-kit/src/wire.ts` changes exactly one line, and it is a doc comment in the `launch.kind` table. `launcher_ui` is a bare string validated host-side, so nothing to republish. ## Seven behavioural breaking changes, none of them a version step The wire-budget redefinition · `FLOOR_KBPS` 5000 → 2000 · DSCP defaults to AUTO (local peers only) · **GameStream video *and* control encryption default on** — both graduated only after their own on-glass pass on `.173`, and in both the client opted in unprompted on a LAN · PyroWave forces Automatic bitrate · the console pairing routes move behind the console password · deep links auto-dial by stable record id only. ## A CHANGELOG entry was filed under a frozen section `ae13b29a` (`launcher_ui` gains `heroic-console`, #402) landed **after** the v0.31.4 tag, but its CHANGELOG block was appended to the **v0.31.4 section** — so a shipped release's notes described a change it does not contain. Moved into the v0.32.0 section verbatim. ## Release notes are written to ISO 24495 plain language Sentences under 30 words, paragraphs under six sentences, bold bullet lead-ins, an explicit overview, a table of contents worded identically to its headings, and acronyms defined on first use. The plugin's own auditor reports **0 findings on the Play notes** and **2 on the release notes** — the words NVIDIA and AMD, which are company names rather than acronyms, left unexpanded deliberately. ## Gates on the cut (the Mac) - `cargo fmt --all --check` - `cargo metadata --offline`; lock diff **versions-only 36/36** - `cargo test -p punktfunk-core --lib --features quic` — **503 passed / 0 failed** - **C ABI harness PASSED (`abi_version=26`)**; the regenerated header is not stale - `scripts/xcheck.sh windows clippy` and `scripts/xcheck.sh linux clippy` — both **exit 0** - `scripts/ci/check-docs-drift.sh`, `scripts/ci/check-docs-links.sh` - Play notes gate **412/500 chars**, unique against 15 files - Both `openapi.json` copies `cmp` identical, stamped 0.32.0 - `cargo audit` **green** — h2 0.4.18 → 0.4.19 closed RUSTSEC-2026-0258, left open deliberately at the v0.31.1 cut ⚠️ `api/openapi.json` is **re-stamped, not regenerated** (`punktfunk-host` does not build on macOS). It is content-identical to v0.31.4, so there is nothing to regenerate. ## After merging Re-read main's tip **before** pushing the tag — it moved twice during this prep alone. Then tag, wait for CI green with no run in flight on the merge sha, and dispatch `announce` with the tag.
enricobuehler added 1 commit 2026-08-27 17:33:54 +00:00
chore(release): bump workspace version to 0.32.0
ci / bun-nix (pull_request) Successful in 35s
ci / docs-drift (pull_request) Successful in 39s
ci / web (pull_request) Successful in 1m15s
ci / docs-site (pull_request) Successful in 1m38s
apple / swift (pull_request) Successful in 2m12s
apple / distribute (pull_request) Skipped
apple / screenshots (pull_request) Skipped
windows-client / client (arm64, --no-default-features, aarch64-pc-windows-msvc, C:\t-a64) (pull_request) Successful in 2m59s
ci / rust-arm64 (pull_request) Successful in 4m11s
android / android (pull_request) Canceled after 6m3s
ci / rust (pull_request) Canceled after 6m10s
nix / flake (pull_request) Canceled after 5m51s
windows-client / client (x64, , x86_64-pc-windows-msvc, C:\t) (pull_request) Canceled after 3m18s
92de6a8ce3
90 commits since v0.31.4 (63 non-merge), cut at db0f4f1e.

**The C ABI steps 25 -> 26.** `punktfunk_connect_opts` closes the
`punktfunk_connect_ex*` ladder: eleven generations each added a field by minting
a new exported symbol plus a 20-something-parameter forwarding shim, and the
replacement takes every option in ONE size-prefixed `PunktfunkConnectOpts`.
Every `ex` keeps its symbol and behaviour, so this is an added surface, not a
changed one. Layout is locked padding-free on both pointer widths (96/68 B) by
const asserts AND by the C harness compiling the same sizes.

The minor bump was already right before the ABI moved: ABR Phase 4 redefines
every control-plane bitrate as the TOTAL WIRE BUDGET rather than the encoder
rate, deliberately without a capability handshake. `include/punktfunk_core.h`
documents the 10-bit SDR ask as "0.32" and says "every pre-0.32 client sets the
two bits together", so the tree had pre-decided the number.

Version table, re-derived at HEAD rather than read off the diff:
  * wire 2 -- unchanged. Three additive, ignorable additions: USER_FLAG_REPEAT,
    CLIENT_CAP_KEEP_HOST_AUDIO, and Welcome::host_caps2 (trailing, absent -> 0)
    carrying HOST_CAP2_REPEAT_MARK
  * C ABI 25 -> 26 (above)
  * crate dirs 27, 39 members, edition 2024, MSRV 1.85 -- unchanged
  * driver proto 6, gamepad channel 3, plugin index 1, host event schema 1,
    gamescope +pfhdr8, SDK 0.1.6 -- all unchanged; pf-driver-proto and
    punktfunk-host/src/events.rs show no diff at all
  * api/openapi.json 0.31.4 -> 0.32.0, content-identical (info.version is the
    whole diff); both copies re-stamped and byte-identical to each other
  * @punktfunk/plugin-kit stays 0.4.4 ON PURPOSE: plugin-kit/src/wire.ts changes
    exactly one line and it is a doc comment in the launch.kind table.
    `launcher_ui` is a bare string validated host-side, so nothing to republish.

The CHANGELOG's `heroic-console` entry was filed under v0.31.4, but ae13b29a
landed AFTER that tag -- so that frozen section described a change v0.31.4 does
not contain. Moved into the v0.32.0 section verbatim.

Seven behavioural breaking changes, none of them a version step, all listed in
CHANGELOG.md: the wire-budget redefinition; FLOOR_KBPS 5000 -> 2000; DSCP
defaults to AUTO (local peers only); GameStream video AND control encryption
default on (both graduated after their own on-glass pass on .173, and
`PUNKTFUNK_GS_ENCRYPT=video` is the new middle rung); PyroWave forces Automatic
bitrate; the console pairing routes move behind the console password (the
security review's critical finding -- a console session cookie alone reached
code execution); deep links auto-dial by stable record id only.

Release notes rewritten to the ISO 24495-1/-5 plain-language rules: sentences
under 30 words, paragraphs under 6 sentences, bold bullet lead-ins, an explicit
overview, a table of contents, and acronyms defined on first use. The plugin's
own text auditor reports 0 findings on the Play notes and 2 on the release
notes, both of which are the words NVIDIA and AMD -- company names, not
acronyms, deliberately left unexpanded.

`cargo audit` stays GREEN: h2 0.4.18 -> 0.4.19 closed RUSTSEC-2026-0258, left
open deliberately at the v0.31.1 cut. Only two allowed `unmaintained` warnings
remain (audiopus_sys, paste).

The Play whatsnew leads with the Android black screen, which is the one fault a
phone or TV user in this cycle could not work around.

Gates on the cut (the Mac): cargo fmt --all --check; cargo metadata --offline;
lock diff versions-only 36/36; cargo test -p punktfunk-core --lib --features
quic 503 passed / 0 failed; the C ABI harness PASSED (abi_version=26); the
regenerated header is not stale; check-docs-drift.sh; check-docs-links.sh; the
Play notes gate 412/500 chars and unique against 15 files; both openapi copies
cmp identical; repo voice scan clean; cargo audit green.
enricobuehler added 1 commit 2026-08-27 17:39:55 +00:00
fix(capture/stall): the classification test's path overshot by one module
ci / bun-nix (pull_request) Successful in 30s
ci / docs-drift (pull_request) Successful in 39s
ci / web (pull_request) Successful in 1m11s
ci / rust-arm64 (pull_request) Successful in 1m32s
ci / docs-site (pull_request) Successful in 1m14s
apple / swift (pull_request) Successful in 2m9s
apple / distribute (pull_request) Skipped
apple / screenshots (pull_request) Skipped
windows-client / client (arm64, --no-default-features, aarch64-pc-windows-msvc, C:\t-a64) (pull_request) Successful in 3m1s
ci / rust (pull_request) Failing after 5m54s
android / android (pull_request) Successful in 7m57s
nix / flake (pull_request) Successful in 7m39s
windows-client / client (x64, , x86_64-pc-windows-msvc, C:\t) (pull_request) Successful in 6m32s
87cb431437
`super::super::stall::classify` in `idd_push.rs`'s test module resolves to
`windows::stall`, which does not exist — `mod stall;` is declared at
`idd_push.rs:332`, so `stall` is a child of `idd_push`, and the test module is
its sibling. A nested `fn` does not add a module level for path resolution,
which is what the extra `super::` was reaching for. The `use
super::stall::{ProbeWindow, StallClass, StallVerdict}` two lines above, in the
same test fn, already had the depth right.

Windows-only and test-only, so it reached main behind a green Build: the error
appears solely under `--all-targets`, when the lib TEST target is compiled.
`package`'s "Clippy (host + tray, Windows)" step has failed on every push since
`11217341` landed it (#413), which is the same shape as #408 — a Windows break
the Mac and Linux dev loops cannot see. The commit that introduced it verified
the logic by extracting `stall.rs` into a native scratch crate, so this test was
never compiled for Windows before it merged.

Reproduced and fixed with `scripts/xcheck.sh`, which covers exactly this and
takes seconds on the Mac:

  scripts/xcheck.sh windows clippy   # broken: exit 101, error[E0433] at :2528
  scripts/xcheck.sh windows clippy   # fixed:  exit 0
  scripts/xcheck.sh linux   clippy   # exit 0

Rides in the 0.32.0 release branch because it blocks the Windows installer: the
`package` job fails before "Pack + sign installer", so a tag cut over this main
would publish a release with no Windows host attached.
enricobuehler added 2 commits 2026-08-27 18:55:57 +00:00
`ci.yml`'s `cargo test --workspace --locked` has been failing at the very last
step for weeks, on a crate the diff never touched:

    Doc-tests pf_capture
    error[E0463]: can't find crate for `pf_frame`
      --> crates/pf-capture/src/lib.rs:11:5

`pf-frame` is an unconditional dependency of `pf-capture`, the rustdoc command
line carries `--extern pf_frame=<path>`, and Format, both Clippy legs and Build
had all compiled `pf-capture` seconds earlier. The path simply no longer existed
by the time rustdoc opened it.

`tests/c_abi.rs::ensure_staticlib` is what removed it. It shells out to a NESTED
`cargo build -p punktfunk-core --features quic` while the OUTER `cargo test` is
mid-run. That resolves features for one package instead of the workspace union
the outer run resolved, so cargo rebuilds punktfunk-core's subgraph under
different metadata into the SHARED target directory — and the outer run's
pending units, which name `target/<profile>/deps/*.rlib` by explicit `--extern`
path, are left pointing at artifacts that have been replaced. Doctests run last,
so they are what falls over.

The old comment asserted the opposite, and that is the bug in one line:

    // The outer cargo's build lock is released during test execution, so this is safe.

The released lock is why the nested build RUNS. It was never why it is safe.

Fix: build into `target/c-abi-harness/` and read the staticlib from there. The
nested build can no longer perturb the outer one, and the harness keeps doing
exactly what it did. Unconditional `--features quic` stays — that is `9c164aa6`'s
fix for a featureless `.a` being silently reused, and it is orthogonal.

Reproduced and verified on Ubuntu 26.04 (the CI base — 24.04's PipeWire is too
old to compile pf-capture's lib test), one variable at a time:

  cargo test -p punktfunk-core -p pf-capture --locked --no-fail-fast
    before                                 -> exit 101, E0463 on pf_frame
    before, --skip c_abi_harness_round_trips -> exit 0        (isolates the cause)
    after                                  -> exit 0, harness still PASSes
                                              (abi_version=26, 4 frames round-tripped)

Each from a wiped target dir. `cargo test -p punktfunk-core --test c_abi` also
passes on macOS, where the staticlib path moved.

Why it looked intermittent: whether the doctest step is reached at all depends on
what else in the workspace fails first, and `9c164aa6` made the nested build
unconditional — before that it ran only when the `.a` was missing.
docs(changelog): record the nested-cargo doctest breakage in the CI section
ci / bun-nix (pull_request) Successful in 24s
ci / docs-drift (pull_request) Successful in 33s
ci / docs-site (pull_request) Successful in 1m11s
apple / swift (pull_request) Successful in 2m14s
ci / web (pull_request) Successful in 2m15s
apple / distribute (pull_request) Skipped
apple / screenshots (pull_request) Skipped
ci / rust-arm64 (pull_request) Successful in 2m39s
android / android (pull_request) Successful in 7m31s
nix / flake (pull_request) Successful in 7m32s
windows-client / client (x64, , x86_64-pc-windows-msvc, C:\t) (pull_request) Successful in 10m36s
windows-client / client (arm64, --no-default-features, aarch64-pc-windows-msvc, C:\t-a64) (pull_request) Successful in 4m12s
ci / rust (pull_request) Successful in 18m45s
07af2f9e97
enricobuehler merged commit ded8586123 into main 2026-08-27 20:29:14 +00:00
enricobuehler deleted branch worktree-release-0320-prep 2026-08-27 20:29:25 +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#420