0.31.3 — the launch that dropped, the refresh a TV never output, and the probe that choked the link #386

Merged
enricobuehler merged 3 commits from worktree-release-0313-recut into main 2026-08-23 10:32:45 +00:00
Owner

Cut from main at f5931650 (#385 merged, main green). 41 commits since v0.31.2 (26 non-merge), 15 PRs.

Three commits, each reviewable on its own: the h2 security bump, the release itself, then the SDK cut.

The number: a patch

One versioned surface moves, and additively — the management API gains PATCH /clients/{fingerprint}, the RenameClient schema and PairedClient.label, none of which existed before. Everything else is where v0.31.2 left it: WIRE_VERSION 2, C ABI 25 with include/punktfunk_core.h showing no diff at all against the v0.31.2 tag (second release running), driver protocol 6 / min 3, gamepad channel 3, plugin index schema 1, host event schema 1, gamescope +pfhdr8.

@punktfunk/host is re-cut to 0.1.6 so a plugin can actually reach the generated types for that route; @punktfunk/plugin-kit stays at 0.4.4 (nothing under plugin-kit/ has moved).

What is in it

The faults share a family resemblance — a session degrading or ending against something ordinary that nothing was checking:

  • #372 Steam's pre-launch trees latched the game lease, and their exit was then read as the game's — the stream dropped ~10 s into every launch.
  • #373 A fullscreen game mode-setting the virtual display, under both stream loops; no in-place encoder rebuild can converge on it.
  • #377 The forced-keyframe coalesce window was measured in frames, not time — 1118 IDR requests in one 91 s session, 1115 honoured.
  • #378 An Android TV negotiated the refresh its own console pin installed rather than what the panel outputs.
  • #379 A startup capacity probe large enough to black-hole the link it was measuring — 14 s to first video on webOS.
  • #375 A hand-back that never verified the panel came back.
  • #381 A half-minted audio devnode nothing afterwards recognised, so duplicates piled up and uninstall left them.
  • #382 A failed compositor build that unlinked the working one it never replaced, taking HDR with it.
  • Plus #376 / #380 (Android present + input), #384 / #385 (console per-frame cost and the new resolution switch), #374 (Moonlight device labels), #370 / #383 (CI).

Two entries worth a reviewer's attention, both recorded as such in the CHANGELOG:

  • #375 ships without a reproduction. Five scenarios across both distro families on real VMs all recovered cleanly and the first proposed mechanism was disproved on glass. It closes the gap that lets any trigger end as a dark panel rather than guessing at one.
  • #380 is re-implemented from #371's diagnosis, and #371 is not merged. All three faults were real and correctly identified; each fix as sent reached further than the hardware that needed it. The notes credit the diagnosis.

The SDK cut (third commit)

One file is the whole diff since sdk-v0.1.5: sdk/src/gen/punktfunk.ts, carrying the generated client for the new route. It is larger than the feature because regenerating it from the unchanged committed spec already produced a ~700-line diff — the checked-in copy had drifted from its own pinned generator, and nothing in CI regenerates or verifies it (unlike api/openapi.json and include/punktfunk_core.h, both gated).

SDK_VERSION in sdk/src/version.ts moves with package.json. It is hand-maintained — the build sets rootDir: "src" so it cannot import the manifest, and the runner ships as one bundled runner-cli.js with no manifest beside it — and the runner compares it against the SDK installed in the plugins tree to decide whether to reinstall. Shipping 0.1.6 with the constant still reading 0.1.5 would publish the types and then never deliver them; version.test.ts gates that, and sdk-publish.yml re-checks it against the tag.

All four steps sdk-publish.yml runs were run locally, in order: bun install --frozen-lockfile --ignore-scripts clean, bun run typecheck clean, bun test 83 pass / 0 fail (191 expect() calls, 12 files — the same 83 the 0.1.5 cut reported), bun run build clean.

Verified before pushing

check-docs-drift.sh clean · check-docs-links.sh clean · the android.yml Play-notes gate run verbatim (444/500 chars, unique) · both openapi copies cmp identical and stamped 0.31.3 · cargo fmt --all --check clean · cargo audit clean over all five Rust lockfiles · cargo about --fail clean · git diff v0.31.2..HEAD empty on both include/punktfunk_core.h and crates/pf-driver-proto, which is the direct evidence for those two version rows · notes voice scan clean (zero backticked terms above ## For developers) and the CHANGELOG link pinned to src/tag/v0.31.3.

Not run here, and why: any punktfunk-host build, clippy or cargo test — the host does not compile on macOS and CI covers it; the web/ and docs-site/ bun builds — nothing under web/ is touched and the docs edit is prose in an existing .md; the Android unit tests — nothing here touches Kotlin.

Docs freshness

#379, #380 and #384 carried their own docs-site updates. The one fact left owed was naming a Moonlight device — a paragraph goes into Managing paired devices in pairing.md. No new PUNKTFUNK_* variable this cycle, no new subcommand, no install-command / URL / port change, so data/platforms.json and the website's vendored copy need nothing.

One thing still left as a decision

THIRD-PARTY-NOTICES.txt still records h2 0.4.15. Regenerating it on macOS drops nine crates (the rusqlite / libsqlite3-sys / fallible-iterator cluster, 575 → 566) because the generator walks the closure of the machine it runs on — that would delete attributions a Linux or Windows build genuinely links. Regenerate on Linux. Nothing gates the checked-in copy and every packaging script regenerates it on its own platform, so shipped packages are accurate.

After merge

Per docs/releases/README.md, two tags on the merge commit:

git tag -a v0.31.3 -F <release notes tag message>   && git push origin v0.31.3
git tag -a sdk-v0.1.6 -m "…"                        && git push origin sdk-v0.1.6

v0.31.3 fans out to the platform builds and seeds the release body from docs/releases/v0.31.3.md; sdk-v0.1.6 triggers sdk-publish.yml alone. The two version independently by design, so the shared commit is a convenience and not a coupling. Then let every platform go green and dispatch announce.yml — that button is the quality gate. Both tags must sit on main, so neither can be pushed before this merges.

Cut from `main` at f5931650 (#385 merged, main green). **41 commits since v0.31.2 (26 non-merge), 15 PRs.** Three commits, each reviewable on its own: the h2 security bump, the release itself, then the SDK cut. ## The number: a patch One versioned surface moves, and additively — the management API gains `PATCH /clients/{fingerprint}`, the `RenameClient` schema and `PairedClient.label`, none of which existed before. Everything else is where v0.31.2 left it: `WIRE_VERSION` 2, C ABI 25 with `include/punktfunk_core.h` showing **no diff at all** against the v0.31.2 tag (second release running), driver protocol 6 / min 3, gamepad channel 3, plugin index schema 1, host event schema 1, gamescope `+pfhdr8`. **`@punktfunk/host` is re-cut to 0.1.6** so a plugin can actually reach the generated types for that route; `@punktfunk/plugin-kit` stays at 0.4.4 (nothing under `plugin-kit/` has moved). ## What is in it The faults share a family resemblance — a session degrading or ending against something ordinary that nothing was checking: - **#372** Steam's pre-launch trees latched the game lease, and their exit was then read as the game's — the stream dropped ~10 s into every launch. - **#373** A fullscreen game mode-setting the virtual display, under both stream loops; no in-place encoder rebuild can converge on it. - **#377** The forced-keyframe coalesce window was measured in frames, not time — 1118 IDR requests in one 91 s session, 1115 honoured. - **#378** An Android TV negotiated the refresh its own console pin installed rather than what the panel outputs. - **#379** A startup capacity probe large enough to black-hole the link it was measuring — 14 s to first video on webOS. - **#375** A hand-back that never verified the panel came back. - **#381** A half-minted audio devnode nothing afterwards recognised, so duplicates piled up and uninstall left them. - **#382** A failed compositor build that unlinked the working one it never replaced, taking HDR with it. - Plus **#376** / **#380** (Android present + input), **#384** / **#385** (console per-frame cost and the new resolution switch), **#374** (Moonlight device labels), **#370** / **#383** (CI). Two entries worth a reviewer's attention, both recorded as such in the CHANGELOG: - **#375 ships without a reproduction.** Five scenarios across both distro families on real VMs all recovered cleanly and the first proposed mechanism was disproved on glass. It closes the gap that lets *any* trigger end as a dark panel rather than guessing at one. - **#380 is re-implemented from #371's diagnosis, and #371 is not merged.** All three faults were real and correctly identified; each fix as sent reached further than the hardware that needed it. The notes credit the diagnosis. ## The SDK cut (third commit) One file is the whole diff since `sdk-v0.1.5`: `sdk/src/gen/punktfunk.ts`, carrying the generated client for the new route. It is larger than the feature because regenerating it from the **unchanged** committed spec already produced a ~700-line diff — the checked-in copy had drifted from its own pinned generator, and nothing in CI regenerates or verifies it (unlike `api/openapi.json` and `include/punktfunk_core.h`, both gated). `SDK_VERSION` in `sdk/src/version.ts` moves with `package.json`. It is hand-maintained — the build sets `rootDir: "src"` so it cannot import the manifest, and the runner ships as one bundled `runner-cli.js` with no manifest beside it — and the runner compares it against the SDK installed in the plugins tree to decide whether to reinstall. Shipping 0.1.6 with the constant still reading 0.1.5 would publish the types and then never deliver them; `version.test.ts` gates that, and `sdk-publish.yml` re-checks it against the tag. All four steps `sdk-publish.yml` runs were run locally, in order: `bun install --frozen-lockfile --ignore-scripts` clean, `bun run typecheck` clean, `bun test` **83 pass / 0 fail** (191 expect() calls, 12 files — the same 83 the 0.1.5 cut reported), `bun run build` clean. ## Verified before pushing `check-docs-drift.sh` clean · `check-docs-links.sh` clean · the `android.yml` Play-notes gate run verbatim (444/500 chars, unique) · both openapi copies `cmp` identical and stamped 0.31.3 · `cargo fmt --all --check` clean · `cargo audit` clean over all five Rust lockfiles · `cargo about --fail` clean · `git diff v0.31.2..HEAD` empty on both `include/punktfunk_core.h` and `crates/pf-driver-proto`, which is the direct evidence for those two version rows · notes voice scan clean (zero backticked terms above `## For developers`) and the CHANGELOG link pinned to `src/tag/v0.31.3`. Not run here, and why: any `punktfunk-host` build, clippy or `cargo test` — the host does not compile on macOS and CI covers it; the `web/` and `docs-site/` bun builds — nothing under `web/` is touched and the docs edit is prose in an existing `.md`; the Android unit tests — nothing here touches Kotlin. ## Docs freshness #379, #380 and #384 carried their own docs-site updates. The one fact left owed was naming a Moonlight device — a paragraph goes into **Managing paired devices** in `pairing.md`. No new `PUNKTFUNK_*` variable this cycle, no new subcommand, no install-command / URL / port change, so `data/platforms.json` and the website's vendored copy need nothing. ## One thing still left as a decision **`THIRD-PARTY-NOTICES.txt` still records `h2 0.4.15`.** Regenerating it on macOS *drops* nine crates (the `rusqlite` / `libsqlite3-sys` / `fallible-iterator` cluster, 575 → 566) because the generator walks the closure of the machine it runs on — that would delete attributions a Linux or Windows build genuinely links. Regenerate on Linux. Nothing gates the checked-in copy and every packaging script regenerates it on its own platform, so shipped packages are accurate. ## After merge Per `docs/releases/README.md`, **two tags on the merge commit**: ``` git tag -a v0.31.3 -F <release notes tag message> && git push origin v0.31.3 git tag -a sdk-v0.1.6 -m "…" && git push origin sdk-v0.1.6 ``` `v0.31.3` fans out to the platform builds and seeds the release body from `docs/releases/v0.31.3.md`; `sdk-v0.1.6` triggers `sdk-publish.yml` alone. The two version independently by design, so the shared commit is a convenience and not a coupling. Then let every platform go green and dispatch `announce.yml` — that button is the quality gate. Both tags must sit on **main**, so neither can be pushed before this merges.
enricobuehler added 2 commits 2026-08-23 10:02:24 +00:00
`cargo audit` on the root lockfile went red on 2026-08-17, when RUSTSEC-2026-0258
was disclosed against h2 <= 0.4.15 (unbounded empty DATA frames; fixed in
0.4.16). audit.yml's cargo-audit job is BLOCKING and fires on every Cargo.lock
change, so the 0.31.3 version bump in the next commit would have taken it red on
merge regardless of this advisory's own timing.

h2 is transitive — no manifest in the workspace declares it — so this is a
lockfile-only change.

MINIMAL ON PURPOSE. `cargo update -p h2` reports "Locking 1 package" but also
rewrote nine unrelated entries from `windows-sys 0.61.2` to 0.52.0/0.59.0,
gratuitous resolver drift that would have changed what the Windows builds compile
against for no reason. That was discarded; the two h2 lines are applied directly
instead, and `cargo metadata --locked` accepts the result with nothing else
moving — which is the proof the resolver needed none of the rest.

VERIFIED: `cargo audit` over all five Rust lockfiles. The root one is now clean;
the other four already were. The two lines cargo-audit still prints
(`audiopus_sys`, `paste`) are *unmaintained* warnings, already allowed via
.cargo/audit.toml, and do not fail the job.

NOT REGENERATED, deliberately: THIRD-PARTY-NOTICES.txt still records h2 0.4.15.
scripts/gen-third-party-notices.sh walks the dependency closure of the machine it
runs on, and on macOS it DROPS nine crates — the rusqlite / libsqlite3-sys /
fallible-iterator / hashlink cluster, 575 -> 566 — because they are gated to
platforms this Mac is not. Committing that would remove attributions a Linux or
Windows build genuinely links, which the script's own header calls a legal
regression rather than an untidiness. Regenerate on Linux. Nothing in
.gitea/workflows diffs the checked-in copy, and build-deb.sh /
pack-host-installer.ps1 / punktfunk.spec / pack-msix.ps1 each regenerate it on
their own platform, so the shipped packages are accurate and this is cosmetic
drift in the in-repo copy only.
release: 0.31.3 — version bump, notes, CHANGELOG, Play notes
ci / docs-site (pull_request) Successful in 1m18s
ci / web (pull_request) Successful in 1m50s
ci / bun-nix (pull_request) Successful in 29s
apple / swift (pull_request) Successful in 2m10s
apple / distribute (pull_request) Skipped
apple / screenshots (pull_request) Skipped
ci / docs-drift (pull_request) Successful in 1m10s
windows-client / client (arm64, --no-default-features, aarch64-pc-windows-msvc, C:\t-a64) (pull_request) Successful in 2m52s
ci / rust-arm64 (pull_request) Successful in 7m2s
android / android (pull_request) Canceled after 8m55s
ci / rust (pull_request) Canceled after 11m10s
nix / flake (pull_request) Canceled after 8m17s
windows-client / client (x64, , x86_64-pc-windows-msvc, C:\t) (pull_request) Canceled after 5m46s
e7ebaf591c
41 commits since v0.31.2 (26 non-merge). Cut from origin/main f5931650 (#385
merged, main green).

THE NUMBER: a patch. One versioned surface moves and it moves additively — the
management API gains PATCH /clients/{fingerprint}, the RenameClient schema and
PairedClient.label, none of which existed before, so nothing that consumes the
API today changes shape. Everything else is where v0.31.2 left it: WIRE_VERSION
2, C ABI 25 with include/punktfunk_core.h showing NO diff against the v0.31.2 tag
(second release running), driver protocol 6 / min 3 with pf-driver-proto
unchanged, gamepad channel 3, plugin index schema 1, host event schema 1,
gamescope +pfhdr8 with no new patch files, SDK 0.1.5 and plugin-kit 0.4.4 both
untouched. Two feat commits (#374, #384), both additive; v0.31.1 carried two
feats as a patch on the same reasoning.

THE SHAPE: the faults share a family resemblance — a session degrading or ending
against something ordinary that nothing was checking. Steam's pre-launch trees
latching the game lease and their exit then read as the game's (#372); a
fullscreen game mode-setting the virtual display under both stream loops (#373);
the forced-keyframe coalesce window measured in frames rather than time (#377);
an Android TV negotiating the refresh its own console pin installed rather than
what the panel outputs (#378); a startup capacity probe large enough to
black-hole the link it was measuring (#379); a hand-back that never verified the
panel came back (#375); a half-minted audio devnode nothing afterwards
recognised (#381); and a failed compositor build that unlinked the working one it
never replaced (#382). Plus two Android input/present fixes (#376, #380), the
console's per-frame cost and its new resolution switch (#384, #385), one feature
(#374), and CI (#370, #383).

TWO ENTRIES WORTH THE READER'S ATTENTION, both recorded as such:
  * #375 ships WITHOUT a reproduction. Five scenarios across both distro families
    on real VMs all recovered cleanly and the first proposed mechanism was
    disproved on glass, so it closes the gap that lets any trigger end as a dark
    panel rather than guessing at one.
  * #380 is re-implemented from #371's diagnosis, and #371 is NOT merged. All
    three faults were real and correctly identified; each fix as sent reached
    further than the hardware that needed it. The notes credit the diagnosis.

DOCS FRESHNESS, per docs/releases/README.md step 1: #379, #380 and #384 carried
their own docs-site updates (configuration.md, input.md, client-settings.md). The
one fact left owed was naming a Moonlight device, whose canonical home is the
"Managing paired devices" section of docs-site/content/docs/pairing.md — a
paragraph goes there. No new PUNKTFUNK_* variable this cycle
(PUNKTFUNK_RECOVER_SESSION_CMD is pre-existing and already documented in
configuration.md and gamescope.md), no new host subcommand, and no install
command, repo URL or port change, so data/platforms.json and the website's
vendored copy need nothing.

VERIFIED HERE: scripts/ci/check-docs-drift.sh clean; scripts/ci/check-docs-links.sh
clean; the android.yml Play notes gate run verbatim, 444/500 characters and unique
against every other release's file; both openapi copies cmp identical and stamped
0.31.3; cargo fmt --all --check clean; cargo audit clean over all five Rust
lockfiles (h2 fixed in the commit below this one); cargo about --fail clean on the
host workspace; git diff v0.31.2..HEAD on include/punktfunk_core.h and on
crates/pf-driver-proto both empty, which is the direct evidence for those two
version rows; Cargo.lock's 36 workspace version strings moved with Cargo.toml;
notes voice scan clean (zero backticked terms above ## For developers) and the
CHANGELOG link pinned to src/tag/v0.31.3.

NOT RUN HERE, and why: any punktfunk-host build, clippy or cargo test — the host
does not compile on macOS at all, and CI covers it; the web/ and docs-site/ bun
builds — nothing under web/ is touched by this commit and the docs-site edit is
prose in an existing .md; the Android unit tests — nothing here touches Kotlin.

LEFT AS A DECISION, not made here: sdk/src/gen/punktfunk.ts changed in #374 (a
clean regeneration that also absorbed ~700 lines of pre-existing drift) but
@punktfunk/host is not re-cut, so the registry's 0.1.5 has no types for the new
route. Cut sdk-v0.1.6 if anything outside this repo needs them.
enricobuehler added 1 commit 2026-08-23 10:13:24 +00:00
chore(sdk): cut 0.1.6 — the rename route's types cannot reach a plugin until they ship
ci / docs-site (pull_request) Successful in 1m19s
apple / swift (pull_request) Successful in 2m9s
apple / distribute (pull_request) Skipped
apple / screenshots (pull_request) Skipped
ci / web (pull_request) Successful in 1m57s
ci / bun-nix (pull_request) Successful in 34s
ci / docs-drift (pull_request) Successful in 33s
windows-client / client (arm64, --no-default-features, aarch64-pc-windows-msvc, C:\t-a64) (pull_request) Successful in 2m41s
ci / rust-arm64 (pull_request) Successful in 4m11s
android / android (pull_request) Successful in 7m10s
windows-client / client (x64, , x86_64-pc-windows-msvc, C:\t) (pull_request) Successful in 6m9s
nix / flake (pull_request) Successful in 8m34s
ci / rust (pull_request) Successful in 18m27s
7312f0ddba
The v0.31.3 CHANGELOG recorded this cut as a decision left open, on the same
reasoning v0.31.0 used for 0.1.5: a plugin resolves `@punktfunk/host` from the
registry, so types sitting in `sdk/` reach nobody until a version carries them.
#374 added `PATCH /clients/{fingerprint}`, `RenameClient` and
`PairedClient.label` to the management API and regenerated the client for them —
so without this cut the route exists on every 0.31.3 host and no plugin can call
it in a typed way.

ONE FILE is the whole diff since sdk-v0.1.5: `sdk/src/gen/punktfunk.ts`. It is
larger than the feature because regenerating it from the UNCHANGED committed spec
already produced a ~700-line diff — the checked-in copy had drifted from its own
pinned generator, and nothing in CI regenerates or verifies it (unlike
api/openapi.json and include/punktfunk_core.h, which are both gated). #374 landed
the clean regeneration rather than hand-patching generated code, and this cut
publishes it.

`SDK_VERSION` moves with `package.json`. It is a hand-maintained constant — the
build sets `rootDir: "src"` so it cannot import the manifest, and the runner ships
as one bundled `runner-cli.js` with no manifest beside it — and the runner
compares it against the SDK installed in the plugins tree to decide whether to
reinstall. Shipping 0.1.6 with the constant still reading 0.1.5 would publish the
types and then never deliver them. `version.test.ts` gates exactly that, which is
also what sdk-publish.yml's "Tag matches package version" step re-checks against
the tag.

GATES, all four steps sdk-publish.yml runs, in order and locally:
`bun install --frozen-lockfile --ignore-scripts` clean, `bun run typecheck`
clean, `bun test` 83 pass / 0 fail / 191 expect() calls across 12 files (the same
83 the 0.1.5 cut reported), `bun run build` clean. Nothing but the two version
sites and the two release documents is touched — no dist/ or lockfile churn
reached the tree.

`@punktfunk/plugin-kit` is deliberately NOT re-cut: nothing under plugin-kit/ has
moved since 0.4.4, which stays the registry's `latest`.

Tag `sdk-v0.1.6` on the merge commit, alongside `v0.31.3`. The two version
independently by design — sdk-publish.yml triggers on `sdk-v*` and the app's `v*`
tags never republish the SDK — so the shared commit is a convenience, not a
coupling.
enricobuehler merged commit 5e30805490 into main 2026-08-23 10:32:45 +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#386