Release cut for v0.28.0 — 180 commits since v0.27.0, based on 9c133350.
Same shape as #159 (0.27.0): version bump + docs/releases/v0.28.0.md + CHANGELOG section + Play whatsnew, in one commit. Merge this, then tag the merge commit — the tag must point at a commit on main, which is how v0.27.0 was cut.
The number
0.28.0, not 0.27.1. The CHANGELOG's in-development section was titled v0.27.1, which the release does not support: 17 feat(…) commits, a packager-visible default flip (GameStream opt-in on every route), the edition-2024 MSRV rise, and a genuinely breaking host change (the built-in library scanners are deleted). scripts/ci/pf-version.sh agrees independently — CI already stamps canaries 0.28.<run>.
Three pre-existing defects fixed while preparing
ABI_VERSION was stale at 18. Two exported symbols landed since v0.27.0 without a bump (punktfunk_connection_note_frame_index_ex, punktfunk_reanchor_gate_arm_expecting_drops; 72 → 74 declarations). The constant's own doc history makes the rule explicit — v17 and v18 each bumped for adding exactly one symbol. Now 19, with its doc entry; header regenerated (cbindgen, CI-gated).
docs-site/public/openapi.json had drifted to 0.21.0, missing five endpoints — the published API reference described a host nobody was running. It is a documented manual copy that nothing in CI enforces, and it drifted a second time inside this cycle when the scanner-removal regen updated api/openapi.json alone. Re-synced twice; the CHANGELOG now says to treat the copy as part of regenerating.
The identity section still said the P-256 key was "generated by ring via rcgen", contradicting this same document's "ring is gone from the tree entirely". Corrected to match 92db6651.
Breaking change, stated in both halves
The six built-in library scanners are deleted; the library is assembled entirely by plugins. No migration by construction — a plugin claims its store and republishes each title under the same <store>:<external_id> id, so entry ids, GameStream app ids, art caches, Moonlight pins, per-source toggles and per-entry hides all keep working. The whole upgrade note: a host with no library plugins installed has an empty grid.
Published deprecation
The notes commit to 2026-10-01 for removing the native plane's legacy-RSA fallback, tracked as #201 (deadline set). Scoped deliberately: the GameStream/Moonlight RSA identity is never dropped — Moonlight mandates RSA and its pairing hashes bind the cert's X.509 signature bytes.
Gates (all green on this commit)
gate
result
cargo fmt --all --check
clean
cargo metadata --locked
OK
Cargo.lock
versions-only, 36/36 lines
cargo test -p punktfunk-core
210 passed
C ABI harness
PASS, prints abi_version=19
docs-site build
exit 0
Play notes gate
440/500 characters, not byte-identical to any other release
notes voice check
0 hits above ## For developers; TL;DR at 6 bullets
On-glass validation
Against the canary of the same main head (code-identical bar ABI_VERSION, which nothing compares at runtime; wire stays 2):
Windows .173 0.28.13309 + Android 0.26.0 (76 fps, 13.3 ms p50) and iPad 0.27.0 (3200×2400)
Linux .21 0.28.0-0.00013300 + iPhone 0.27.0 (79 fps, 6.7 ms p50)
Two release claims proven rather than asserted:
Idle sleep-blocker fix, before/after on .173 with nothing connected: powercfg /requests → SYSTEM went from [DRIVER] Steam Streaming Microphone … Ein Audiostream wird derzeit verwendet. to Keine.
GameStream flip at the socket level on .21: 9777/udp + 47990/tcp present, 47984/47989/47999 absent by default; PUNKTFUNK_GAMESTREAM=1 restores all three; reverting removes them.
Old-client compat holds across two releases: Android 0.26.0 streams fine against the 0.28.0 host.
⏳Not re-validated: the Mutter two-client fix (a5c9b7b8). That crash was found during this release's validation and A/B'd against released 0.27.0 (byte-identical, so never a regression — the fix's own commit message cites that A/B). Re-running it needs .21, whose VM is stopped and shares a passed-through GPU with home-bazzite-2. Repro when free: iPhone 2868×1320 → SIGTERM → Android 2800×1260, gnome-shell PID must not change; confirm the build carries the fix with strings /usr/bin/punktfunk-host | grep "waited out a monitor-topology rebuild".
After merging
Tag the merge commit v0.28.0 (annotated) and push the tag — the build workflows fan out and seed the release body from docs/releases/v0.28.0.md.
Wait for every platform to go green.
Dispatch the announce workflow with v0.28.0. Pressing "go" is the quality gate; it re-asserts the notes file over the live release and posts to Discord #releases.
Release cut for **v0.28.0** — 180 commits since v0.27.0, based on `9c133350`.
Same shape as #159 (0.27.0): version bump + `docs/releases/v0.28.0.md` + CHANGELOG section + Play `whatsnew`, in one commit. **Merge this, then tag the merge commit** — the tag must point at a commit on `main`, which is how v0.27.0 was cut.
## The number
0.28.0, not 0.27.1. The CHANGELOG's in-development section was titled `v0.27.1`, which the release does not support: 17 `feat(…)` commits, a packager-visible default flip (GameStream opt-in on every route), the edition-2024 MSRV rise, and a genuinely **breaking** host change (the built-in library scanners are deleted). `scripts/ci/pf-version.sh` agrees independently — CI already stamps canaries `0.28.<run>`.
## Three pre-existing defects fixed while preparing
1. **`ABI_VERSION` was stale at 18.** Two exported symbols landed since v0.27.0 without a bump (`punktfunk_connection_note_frame_index_ex`, `punktfunk_reanchor_gate_arm_expecting_drops`; 72 → 74 declarations). The constant's own doc history makes the rule explicit — v17 and v18 each bumped for adding exactly one symbol. Now **19**, with its doc entry; header regenerated (cbindgen, CI-gated).
2. **`docs-site/public/openapi.json` had drifted to `0.21.0`**, missing five endpoints — the published API reference described a host nobody was running. It is a documented manual copy that nothing in CI enforces, and it **drifted a second time inside this cycle** when the scanner-removal regen updated `api/openapi.json` alone. Re-synced twice; the CHANGELOG now says to treat the copy as part of regenerating.
3. **The identity section still said the P-256 key was "generated by ring via rcgen"**, contradicting this same document's "ring is gone from the tree entirely". Corrected to match `92db6651`.
## Breaking change, stated in both halves
The six built-in library scanners are **deleted**; the library is assembled entirely by plugins. No migration by construction — a plugin claims its store and republishes each title under the same `<store>:<external_id>` id, so entry ids, GameStream app ids, art caches, Moonlight pins, per-source toggles and per-entry hides all keep working. **The whole upgrade note: a host with no library plugins installed has an empty grid.**
## Published deprecation
The notes commit to **2026-10-01** for removing the native plane's legacy-RSA fallback, tracked as #201 (deadline set). Scoped deliberately: the **GameStream/Moonlight RSA identity is never dropped** — Moonlight mandates RSA and its pairing hashes bind the cert's X.509 signature bytes.
## Gates (all green on this commit)
| gate | result |
|---|---|
| `cargo fmt --all --check` | clean |
| `cargo metadata --locked` | OK |
| `Cargo.lock` | versions-only, 36/36 lines |
| `cargo test -p punktfunk-core` | 210 passed |
| C ABI harness | PASS, prints `abi_version=19` |
| docs-site build | exit 0 |
| Play notes gate | 440/500 **characters**, not byte-identical to any other release |
| notes voice check | 0 hits above `## For developers`; TL;DR at 6 bullets |
## On-glass validation
Against the canary of the same main head (code-identical bar `ABI_VERSION`, which nothing compares at runtime; wire stays 2):
- **Windows .173 `0.28.13309`** + Android 0.26.0 (76 fps, 13.3 ms p50) and iPad 0.27.0 (3200×2400)
- **Linux .21 `0.28.0-0.00013300`** + iPhone 0.27.0 (79 fps, 6.7 ms p50)
Two release claims proven rather than asserted:
- **Idle sleep-blocker fix**, before/after on .173 with nothing connected: `powercfg /requests` → SYSTEM went from `[DRIVER] Steam Streaming Microphone … Ein Audiostream wird derzeit verwendet.` to `Keine.`
- **GameStream flip at the socket level** on .21: 9777/udp + 47990/tcp present, 47984/47989/47999 absent by default; `PUNKTFUNK_GAMESTREAM=1` restores all three; reverting removes them.
Old-client compat holds across two releases: Android **0.26.0** streams fine against the 0.28.0 host.
⏳ **Not re-validated:** the Mutter two-client fix (`a5c9b7b8`). That crash was found during this release's validation and A/B'd against released 0.27.0 (byte-identical, so never a regression — the fix's own commit message cites that A/B). Re-running it needs .21, whose VM is stopped and shares a passed-through GPU with `home-bazzite-2`. Repro when free: iPhone 2868×1320 → SIGTERM → Android 2800×1260, `gnome-shell` PID must not change; confirm the build carries the fix with `strings /usr/bin/punktfunk-host | grep "waited out a monitor-topology rebuild"`.
## After merging
1. Tag the merge commit `v0.28.0` (annotated) and push the tag — the build workflows fan out and seed the release body from `docs/releases/v0.28.0.md`.
2. Wait for every platform to go green.
3. Dispatch the `announce` workflow with `v0.28.0`. Pressing "go" is the quality gate; it re-asserts the notes file over the live release and posts to Discord `#releases`.
180 commits since v0.27.0. Cut from origin/main 9c133350.
THE NUMBER: 0.28.0, not 0.27.1. The CHANGELOG's in-development section was
titled "v0.27.1", which the release does not support — 17 `feat(...)` commits,
a packager-visible default flip (GameStream opt-in on every route), the
edition-2024 MSRV rise, and now a genuinely BREAKING host change (the built-in
library scanners are deleted). `scripts/ci/pf-version.sh`'s canary rule agrees
independently: CI already stamps canaries `0.28.<run>`.
TWO DEFECTS FOUND AND FIXED WHILE PREPARING, both pre-existing on main:
1. C ABI_VERSION was stale at 18. Two exported symbols landed since v0.27.0
without a bump — punktfunk_connection_note_frame_index_ex and
punktfunk_reanchor_gate_arm_expecting_drops (72 -> 74 declarations in
include/punktfunk_core.h). The constant's own doc history makes the rule
explicit: v17 and v18 each bumped for adding exactly one symbol. Bumped to 19
with its doc entry; the header is regenerated (cbindgen, CI-gated) and the
C ABI harness passes printing abi_version=19.
2. docs-site/public/openapi.json had drifted to 0.21.0 against api/openapi.json,
missing five endpoints. The copy is a documented manual step that nothing in
CI enforces (CONTRIBUTING.md says so outright). Re-synced — and then it
DRIFTED AGAIN inside this same cycle when the scanner-removal regen updated
api/openapi.json alone, so it is re-synced a second time and the CHANGELOG
now says to treat the copy as part of regenerating, not a follow-up.
⭐ The final docs batch also invalidated a line in this CHANGELOG: the identity
section still said the P-256 key was "generated by ring via rcgen", which contradicted
this same document's "ring is gone from the tree entirely". Corrected to "rcgen on the
workspace's aws-lc-rs backend", matching 92db6651.
api/openapi.json stays stamped 0.27.0: it cannot be regenerated here
(punktfunk-host does not compile on macOS) and does not need to be — the drift
test normalizes info.version, so only the SURFACE is gated, and the surface is
current.
CHANGELOG: retitled to v0.28.0, gained the version table (wire 2 unchanged; C
ABI 18->19; edition 2021->2024 and MSRV 1.82->1.85; driver protocol 6 and
gamepad channel 3 unchanged; plugin-kit 0.4.0->0.4.1), a breaking-changes
section, and ~29 topics the in-development text predated — including the four
that landed last: the scanner->plugin migration, the Mutter rebuild
serialization, the KWin <=60 Hz readback, and the Apple/Android de-prime fuse.
⭐ THE BREAKING ONE, stated plainly in both halves: the six built-in library
scanners are DELETED and the library is assembled entirely by plugins. There is
deliberately no migration — a plugin claims its store and republishes each title
under the same `<store>:<external_id>` id, so entry ids, GameStream app ids, art
caches, Moonlight pins, per-source toggles and per-entry hides all keep working.
The one visible consequence, and the whole upgrade note: a host with NO library
plugins installed has an empty grid.
⭐⭐ The Mutter two-client segfault this release now fixes (a5c9b7b8) is the one
found during THIS release's on-glass validation: chaining two clients through a
kept display killed gnome-shell in meta_monitor_manager_rebuild. It was A/B'd on
.21 against the released 0.27.0 and shown byte-identical there, so it was never
a 0.28.0 regression — and the fix's own commit message cites that A/B.
GATES RUN, all green on this commit (re-run after the rebase onto 86cbbea0):
cargo fmt --all --check clean
cargo metadata --locked OK against the new dependency tree
Cargo.lock versions-only vs origin/main, 36/36 lines
cargo test -p punktfunk-core 210 passed
c_abi harness PASS, abi_version=19 (needs LIBRARY_PATH
for opus on macOS; a link path, not a defect)
docs-site build exit 0 (bun install --frozen-lockfile + build)
Play notes gate 440/500 CHARACTERS, not byte-identical to
any other release (`•` is 3 bytes — count
characters, as the gate does)
notes voice check 0 hits above `## For developers`; TL;DR at
6 bullets (README caps it at six)
ON-GLASS (against the canary of 14425716, code-identical bar ABI_VERSION):
Windows .173 0.28.13309 + Android and iPad, Linux .21 0.28.0-0.00013300 +
iPhone — both PASS. The idle sleep-blocker fix is proven before/after on .173
(`powercfg /requests` SYSTEM: the mic devnode -> "Keine."), and the GameStream
flip is proven at the socket level on .21 (47984/47989/47999 absent by default,
restored by PUNKTFUNK_GAMESTREAM=1). Old-client compat holds: Android 0.26.0
streams against the 0.28.0 host.
⏳ NOT re-validated: the Mutter fix itself. .21 (VM 103) is stopped — it and
home-bazzite-2 (VM 119, currently running) share one passed-through GPU, so
bringing .21 up would stop the other VM. Owed once .21 is free; the repro is
iPhone 2868x1320 -> SIGTERM -> Android 2800x1260, and the marker to confirm the
build carries the fix is the string "mutter: waited out a monitor-topology
rebuild before releasing the lock".
NOT INCLUDED: the 14 unpushed pf-capture/pf-vdisplay sweep commits on the local
main. Never through CI; pushing them is the user's call.
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
Release cut for v0.28.0 — 180 commits since v0.27.0, based on
9c133350.Same shape as #159 (0.27.0): version bump +
docs/releases/v0.28.0.md+ CHANGELOG section + Playwhatsnew, in one commit. Merge this, then tag the merge commit — the tag must point at a commit onmain, which is how v0.27.0 was cut.The number
0.28.0, not 0.27.1. The CHANGELOG's in-development section was titled
v0.27.1, which the release does not support: 17feat(…)commits, a packager-visible default flip (GameStream opt-in on every route), the edition-2024 MSRV rise, and a genuinely breaking host change (the built-in library scanners are deleted).scripts/ci/pf-version.shagrees independently — CI already stamps canaries0.28.<run>.Three pre-existing defects fixed while preparing
ABI_VERSIONwas stale at 18. Two exported symbols landed since v0.27.0 without a bump (punktfunk_connection_note_frame_index_ex,punktfunk_reanchor_gate_arm_expecting_drops; 72 → 74 declarations). The constant's own doc history makes the rule explicit — v17 and v18 each bumped for adding exactly one symbol. Now 19, with its doc entry; header regenerated (cbindgen, CI-gated).docs-site/public/openapi.jsonhad drifted to0.21.0, missing five endpoints — the published API reference described a host nobody was running. It is a documented manual copy that nothing in CI enforces, and it drifted a second time inside this cycle when the scanner-removal regen updatedapi/openapi.jsonalone. Re-synced twice; the CHANGELOG now says to treat the copy as part of regenerating.92db6651.Breaking change, stated in both halves
The six built-in library scanners are deleted; the library is assembled entirely by plugins. No migration by construction — a plugin claims its store and republishes each title under the same
<store>:<external_id>id, so entry ids, GameStream app ids, art caches, Moonlight pins, per-source toggles and per-entry hides all keep working. The whole upgrade note: a host with no library plugins installed has an empty grid.Published deprecation
The notes commit to 2026-10-01 for removing the native plane's legacy-RSA fallback, tracked as #201 (deadline set). Scoped deliberately: the GameStream/Moonlight RSA identity is never dropped — Moonlight mandates RSA and its pairing hashes bind the cert's X.509 signature bytes.
Gates (all green on this commit)
cargo fmt --all --checkcargo metadata --lockedCargo.lockcargo test -p punktfunk-coreabi_version=19## For developers; TL;DR at 6 bulletsOn-glass validation
Against the canary of the same main head (code-identical bar
ABI_VERSION, which nothing compares at runtime; wire stays 2):0.28.13309+ Android 0.26.0 (76 fps, 13.3 ms p50) and iPad 0.27.0 (3200×2400)0.28.0-0.00013300+ iPhone 0.27.0 (79 fps, 6.7 ms p50)Two release claims proven rather than asserted:
powercfg /requests→ SYSTEM went from[DRIVER] Steam Streaming Microphone … Ein Audiostream wird derzeit verwendet.toKeine.PUNKTFUNK_GAMESTREAM=1restores all three; reverting removes them.Old-client compat holds across two releases: Android 0.26.0 streams fine against the 0.28.0 host.
⏳ Not re-validated: the Mutter two-client fix (
a5c9b7b8). That crash was found during this release's validation and A/B'd against released 0.27.0 (byte-identical, so never a regression — the fix's own commit message cites that A/B). Re-running it needs .21, whose VM is stopped and shares a passed-through GPU withhome-bazzite-2. Repro when free: iPhone 2868×1320 → SIGTERM → Android 2800×1260,gnome-shellPID must not change; confirm the build carries the fix withstrings /usr/bin/punktfunk-host | grep "waited out a monitor-topology rebuild".After merging
v0.28.0(annotated) and push the tag — the build workflows fan out and seed the release body fromdocs/releases/v0.28.0.md.announceworkflow withv0.28.0. Pressing "go" is the quality gate; it re-asserts the notes file over the live release and posts to Discord#releases.