Release prep for v0.31.4. 21 commits since v0.31.3 (14 non-merge), cut from c407f6a6 (#393 merged).
Everything docs/releases/README.md step 1 asks for, authored before the tag: version bump, user notes, CHANGELOG section, Play "What's new", and the docs-freshness pass. The tag is deliberately not pushed — that is step 2, and it fans out every build workflow.
The number: a patch, and the cleanest in a while
Nothing versioned moves. Not one row:
surface
v0.31.3 → v0.31.4
Wire protocol
2 → 2
C ABI
25 → 25 — include/punktfunk_core.h byte-identical to v0.31.3, and to v0.31.2 and v0.31.1. Third release running
Driver protocol
6 / min 3 — pf-driver-proto no diff
Gamepad channel
3 → 3
Plugin index / host event schema
1 → 1 (events.rs no diff)
api/openapi.json
content-identical — info.version is the entire diff. First release since v0.31.0 where the management API grows nothing
gamescope patch level
+pfhdr8, no new patch files
SDK / plugin-kit
0.1.6 / 0.4.4, both untouched
Dependencies
none move, in any of the five Rust lockfiles
Four feat commits, all additive and all client- or Linux-host-side. v0.31.3 and v0.31.1 each carried two feats as a patch on the same reasoning.
The shape
Linux-host, and the faults share a premise: a policy written against one desktop that silently did nothing on the others.
#389 (five commits) — Topology::Exclusive on a gamescope session was org_kde_kwin_dpms and reached only a live KDE desktop, so a Game Mode box (the deployment whose TV the operator most wants dark) got nothing. Now a dispatcher: KDE, sway, Hyprland, and a DRM floor for a box with no desktop at all. Plus the managed route's own hold, and the box-session free that was gated on is_steam_launch. GNOME is a structural gap and says so at warn!.
#391 — the sleep:idle BLOCK inhibitor refused everySuspend() for a stream's length, including the operator's own from Steam's power menu; and on a DM-stopping takeover polkit's allow_active fallback found no session to elect and dropped all three power actions to auth_admin_keep.
#393 — the web console served the legacy identity (every launcher names that pair; none of them can choose), so browsers rejected it outright and the tray's mgmt-pinned probe refused the handshake and labelled a healthy console dead.
#392 — the encode down-driver's thresholds were absolute durations calibrated at 120 Hz, and its stand-down was permanent. A 1440p60 session ratcheted 57 → 5 Mbps over ten minutes on a link that never dropped a packet.
#387 an installTap format snapshot reaching the ObjC terminate handler · #388 the console's bitrate row · #390 a GTK front door for the console UI.
Two entries worth a reviewer's attention
The sway arm of #389 is not verified on glass. The lab has Bazzite, Nobara and SteamOS — no sway box. Hyprland, KDE and the DRM floor were all measured on real machines. The notes say so in "Thanks" rather than burying it.
#389's Hyprland arm shipped wrong in its first cut and was corrected on glass (540e282e): the dpms dispatcher is a toggle that ignores the state word, so the blind "send off, later send on" would have lit an already-dark head at stream start and darkened a lit one at teardown — the exact failure the feature exists to prevent.
Docs freshness
#390 and #393 carried their own docs-site updates. Two facts were left owed, both in gamescope.md, both the "stale sentence" class docs-drift cannot catch:
the darkening mechanism list still read "KDE, else DRM" and never learned about the sway and Hyprland arms;
the "could not deliver a dark screen" example was a box in Game Mode — now precisely the case that is served. The real remaining gap is GNOME/Mutter, so it says that instead.
Also: the punktfunk group grew a third grant this cycle (the three logind power actions), and its callout block enumerated only the usbip nodes — one sentence added.
No new PUNKTFUNK_* variable, 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
check-docs-drift.sh clean — and proven live rather than assumed: a deliberate one-byte break of the openapi copy makes it fail, and it passes again restored.
check-docs-links.sh clean.
The android.yml Play notes gate run verbatim: 481/500 chars, unique against every other release's file.
Both openapi copies cmp identical and stamped 0.31.4.
cargo fmt --all --check clean; the repo pre-push hook run manually, exit 0.
git diff v0.31.3..HEADempty on include/punktfunk_core.h, crates/pf-driver-proto, punktfunk-core/src/lib.rs, punktfunk-host/src/events.rs and api/openapi.json — direct evidence for five version rows. git diff v0.31.1..HEAD on the C header also empty, which is the "third release running" claim.
Cargo.lock's 36 workspace version strings moved with Cargo.toml; zero 0.31.3 strings remain. 27 crate dirs / 39 workspace members, both unchanged.
Notes voice scan clean: zero backticked terms above ## For developers, and zero hits scanning for gamescope/polkit/logind/DPMS/CRTC/DRM/systemd/kwin/mutter/wlroots/rustls/ioctl.
CHANGELOG link pinned to src/tag/v0.31.4.
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 edits are prose in an existing .md. The Android unit tests — nothing here touches Kotlin.
After merge
git tag -a v0.31.4 … && git push origin v0.31.4 — whichever workflow wins the create race seeds the release body from docs/releases/v0.31.4.md.
Wait for every platform to go green. Watch for the merge-sha gap described in docs/releases/README.md: match your own merge sha in the run list, because "CI ran" is not "your commit ran".
Dispatch the announce workflow with the tag.
Release prep for **v0.31.4**. 21 commits since v0.31.3 (14 non-merge), cut from `c407f6a6` (#393 merged).
Everything `docs/releases/README.md` step 1 asks for, authored **before** the tag: version bump, user notes, CHANGELOG section, Play "What's new", and the docs-freshness pass. **The tag is deliberately not pushed** — that is step 2, and it fans out every build workflow.
## The number: a patch, and the cleanest in a while
**Nothing versioned moves.** Not one row:
| surface | v0.31.3 → v0.31.4 |
|---|---|
| Wire protocol | 2 → 2 |
| C ABI | 25 → 25 — `include/punktfunk_core.h` byte-identical to v0.31.3, **and to v0.31.2 and v0.31.1**. Third release running |
| Driver protocol | 6 / min 3 — `pf-driver-proto` no diff |
| Gamepad channel | 3 → 3 |
| Plugin index / host event schema | 1 → 1 (`events.rs` no diff) |
| `api/openapi.json` | **content-identical** — `info.version` is the entire diff. First release since v0.31.0 where the management API grows nothing |
| gamescope patch level | +pfhdr8, no new patch files |
| SDK / plugin-kit | 0.1.6 / 0.4.4, both untouched |
| Dependencies | **none move**, in any of the five Rust lockfiles |
Four `feat` commits, all additive and all client- or Linux-host-side. v0.31.3 and v0.31.1 each carried two feats as a patch on the same reasoning.
## The shape
Linux-host, and the faults share a premise: **a policy written against one desktop that silently did nothing on the others.**
- **#389** (five commits) — `Topology::Exclusive` on a gamescope session was `org_kde_kwin_dpms` and reached only a live KDE desktop, so a Game Mode box (the deployment whose TV the operator most wants dark) got nothing. Now a dispatcher: KDE, sway, Hyprland, and a DRM floor for a box with no desktop at all. Plus the managed route's own hold, and the box-session free that was gated on `is_steam_launch`. **GNOME is a structural gap and says so at `warn!`.**
- **#391** — the `sleep:idle` BLOCK inhibitor refused *every* `Suspend()` for a stream's length, including the operator's own from Steam's power menu; and on a DM-stopping takeover polkit's `allow_active` fallback found no session to elect and dropped all three power actions to `auth_admin_keep`.
- **#393** — the web console served the **legacy** identity (every launcher names that pair; none of them *can* choose), so browsers rejected it outright and the tray's mgmt-pinned probe refused the handshake and labelled a healthy console dead.
- **#392** — the encode down-driver's thresholds were absolute durations calibrated at 120 Hz, and its stand-down was permanent. A 1440p60 session ratcheted 57 → 5 Mbps over ten minutes on a link that never dropped a packet.
- **#387** an `installTap` format snapshot reaching the ObjC terminate handler · **#388** the console's bitrate row · **#390** a GTK front door for the console UI.
## Two entries worth a reviewer's attention
- **The sway arm of #389 is not verified on glass.** The lab has Bazzite, Nobara and SteamOS — no sway box. Hyprland, KDE and the DRM floor were all measured on real machines. The notes say so in "Thanks" rather than burying it.
- **#389's Hyprland arm shipped wrong in its first cut and was corrected on glass** (`540e282e`): the dpms dispatcher is a **toggle** that ignores the state word, so the blind "send off, later send on" would have *lit* an already-dark head at stream start and *darkened* a lit one at teardown — the exact failure the feature exists to prevent.
## Docs freshness
#390 and #393 carried their own docs-site updates. Two facts were left owed, both in `gamescope.md`, both the "stale sentence" class `docs-drift` cannot catch:
- the darkening mechanism list still read "KDE, else DRM" and never learned about the sway and Hyprland arms;
- the "could not deliver a dark screen" example was *a box in Game Mode* — now precisely the case that **is** served. The real remaining gap is GNOME/Mutter, so it says that instead.
Also: the `punktfunk` group grew a third grant this cycle (the three logind power actions), and its callout block enumerated only the usbip nodes — one sentence added.
No new `PUNKTFUNK_*` variable, 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
- `check-docs-drift.sh` clean — and **proven live rather than assumed**: a deliberate one-byte break of the openapi copy makes it fail, and it passes again restored.
- `check-docs-links.sh` clean.
- The `android.yml` Play notes gate **run verbatim**: 481/500 chars, unique against every other release's file.
- Both openapi copies `cmp` identical and stamped 0.31.4.
- `cargo fmt --all --check` clean; the repo `pre-push` hook run manually, exit 0.
- `git diff v0.31.3..HEAD` **empty** on `include/punktfunk_core.h`, `crates/pf-driver-proto`, `punktfunk-core/src/lib.rs`, `punktfunk-host/src/events.rs` and `api/openapi.json` — direct evidence for five version rows. `git diff v0.31.1..HEAD` on the C header also empty, which is the "third release running" claim.
- `Cargo.lock`'s 36 workspace version strings moved with `Cargo.toml`; zero `0.31.3` strings remain. 27 crate dirs / 39 workspace members, both unchanged.
- Notes voice scan clean: zero backticked terms above `## For developers`, and zero hits scanning for gamescope/polkit/logind/DPMS/CRTC/DRM/systemd/kwin/mutter/wlroots/rustls/ioctl.
- CHANGELOG link pinned to `src/tag/v0.31.4`.
## 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 edits are prose in an existing `.md`. The Android unit tests — nothing here touches Kotlin.
## After merge
1. `git tag -a v0.31.4 … && git push origin v0.31.4` — whichever workflow wins the create race seeds the release body from `docs/releases/v0.31.4.md`.
2. Wait for every platform to go green. Watch for the merge-sha gap described in `docs/releases/README.md`: match your own merge sha in the run list, because "CI ran" is not "your commit ran".
3. Dispatch the `announce` workflow with the tag.
21 commits since v0.31.3 (14 non-merge). Cut from origin/main c407f6a6 (#393
merged).
THE NUMBER: a patch, and the cleanest one in a while — NOTHING versioned moves.
WIRE_VERSION 2, C ABI 25 with include/punktfunk_core.h showing NO diff against
the v0.31.3 tag (nor against v0.31.2 or v0.31.1 — third release running), driver
protocol 6 / min 3 with pf-driver-proto unchanged, gamepad channel 3, plugin
index schema 1, host event schema 1 (events.rs no diff), gamescope +pfhdr8 with
no new patch files, SDK 0.1.6 and plugin-kit 0.4.4 both untouched. api/openapi.json
is CONTENT-identical — info.version is the entire diff, the first release since
v0.31.0 where the management API grows nothing. No lockfile moves, in any of the
five. Four feat commits, all additive and all client- or Linux-host-side; v0.31.3
and v0.31.1 each carried two feats as a patch on the same reasoning.
THE SHAPE: Linux-host, and the faults share a premise — a policy written against
ONE desktop that silently did nothing on the others. Topology::Exclusive on a
gamescope session was implemented as org_kde_kwin_dpms and reached only a live
KDE desktop, so a Game Mode box (the deployment whose TV the operator most wants
dark) got nothing: #389 is five commits closing that — a DRM floor for a box with
no desktop at all, sway and Hyprland arms, the managed route's own hold, and the
box-session free that was gated on is_steam_launch. The sleep:idle BLOCK
inhibitor refused every Suspend() for a stream's length including the operator's
own from Steam's power menu, and on a DM-stopping takeover polkit's allow_active
fallback found no session to elect and dropped all three power actions to
auth_admin_keep (#391). The web console served the LEGACY identity because every
launcher names that pair and none of them CAN choose, so browsers rejected it and
the tray's mgmt-pinned probe refused the handshake and labelled a healthy console
dead (#393). Plus the client's encode down-driver (#392), an installTap format
snapshot reaching the ObjC terminate handler (#387), the console's bitrate row
(#388), and a GTK front door for the console UI (#390).
TWO ENTRIES WORTH THE READER'S ATTENTION, both recorded as such:
* The sway arm of #389 is NOT verified on glass — the lab has Bazzite, Nobara
and SteamOS, no sway box. Hyprland, KDE and the DRM floor were all measured
on real machines. The notes say so in "Thanks" rather than burying it.
* #389's Hyprland arm shipped WRONG in its first cut and was corrected on glass
(540e282e): the dpms dispatcher is a TOGGLE that ignores the state word, so
the blind "send off, later send on" would have LIT an already-dark head at
stream start and DARKENED a lit one at teardown — the exact failure the
feature exists to prevent. Read -> act only if different -> verify.
DOCS FRESHNESS, per docs/releases/README.md step 1: #390 and #393 carried their
own docs-site updates (clients.md, and #389's two gamescope.md edits). Two facts
were left owed, both in gamescope.md and both the "stale sentence" class that
docs-drift cannot catch:
* the darkening mechanism list still read "KDE, else DRM" and never learned
about the sway and Hyprland arms from e0a82201;
* the "could not deliver a dark screen" example was a box in Game Mode, which
is now precisely the case that IS served by the DRM floor. The real remaining
gap is GNOME/Mutter, so it says that instead.
Also: the punktfunk group grew a third grant this cycle (the three logind power
actions, via 49-punktfunk-power.rules), and the group's own callout block
enumerated only the usbip nodes — one sentence added there.
No new PUNKTFUNK_* variable this cycle (PUNKTFUNK_UI_TLS_CERT/KEY are
pre-existing and already in the baseline), 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, and PROVEN LIVE rather than
assumed — a deliberate one-byte break of the openapi copy makes it fail, and it
passes again restored; scripts/ci/check-docs-links.sh clean; the android.yml Play
notes gate run verbatim, 481/500 characters and unique against every other
release's file; both openapi copies cmp identical and stamped 0.31.4; cargo fmt
--all --check clean; git diff v0.31.3..HEAD empty on include/punktfunk_core.h, on
crates/pf-driver-proto, on punktfunk-core/src/lib.rs, on punktfunk-host/src/events.rs
and on api/openapi.json, which is the direct evidence for those five version rows;
git diff v0.31.1..HEAD on the C header also empty, which is the "third release
running" claim; Cargo.lock's 36 workspace version strings moved with Cargo.toml
and zero 0.31.3 strings remain; 27 crate dirs / 39 workspace members, both
unchanged; notes voice scan clean (zero backticked terms above ## For developers,
and zero internal names on a scan for gamescope/polkit/logind/DPMS/CRTC/DRM/
systemd/kwin/mutter/wlroots/rustls/ioctl); CHANGELOG link pinned to
src/tag/v0.31.4.
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 edits are
prose in an existing .md; the Android unit tests — nothing here touches Kotlin.
LEFT AS A DECISION, not made here: the tag itself. Notes, CHANGELOG and Play text
are authored ahead of it exactly as docs/releases/README.md step 1 requires, so
whichever workflow wins the create race seeds a complete release body — but
pushing v0.31.4 fans out every build workflow, and that is step 2.
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 prep for v0.31.4. 21 commits since v0.31.3 (14 non-merge), cut from
c407f6a6(#393 merged).Everything
docs/releases/README.mdstep 1 asks for, authored before the tag: version bump, user notes, CHANGELOG section, Play "What's new", and the docs-freshness pass. The tag is deliberately not pushed — that is step 2, and it fans out every build workflow.The number: a patch, and the cleanest in a while
Nothing versioned moves. Not one row:
include/punktfunk_core.hbyte-identical to v0.31.3, and to v0.31.2 and v0.31.1. Third release runningpf-driver-protono diffevents.rsno diff)api/openapi.jsoninfo.versionis the entire diff. First release since v0.31.0 where the management API grows nothingFour
featcommits, all additive and all client- or Linux-host-side. v0.31.3 and v0.31.1 each carried two feats as a patch on the same reasoning.The shape
Linux-host, and the faults share a premise: a policy written against one desktop that silently did nothing on the others.
Topology::Exclusiveon a gamescope session wasorg_kde_kwin_dpmsand reached only a live KDE desktop, so a Game Mode box (the deployment whose TV the operator most wants dark) got nothing. Now a dispatcher: KDE, sway, Hyprland, and a DRM floor for a box with no desktop at all. Plus the managed route's own hold, and the box-session free that was gated onis_steam_launch. GNOME is a structural gap and says so atwarn!.sleep:idleBLOCK inhibitor refused everySuspend()for a stream's length, including the operator's own from Steam's power menu; and on a DM-stopping takeover polkit'sallow_activefallback found no session to elect and dropped all three power actions toauth_admin_keep.installTapformat snapshot reaching the ObjC terminate handler · #388 the console's bitrate row · #390 a GTK front door for the console UI.Two entries worth a reviewer's attention
540e282e): the dpms dispatcher is a toggle that ignores the state word, so the blind "send off, later send on" would have lit an already-dark head at stream start and darkened a lit one at teardown — the exact failure the feature exists to prevent.Docs freshness
#390 and #393 carried their own docs-site updates. Two facts were left owed, both in
gamescope.md, both the "stale sentence" classdocs-driftcannot catch:Also: the
punktfunkgroup grew a third grant this cycle (the three logind power actions), and its callout block enumerated only the usbip nodes — one sentence added.No new
PUNKTFUNK_*variable, no new host subcommand, and no install command, repo URL or port change, sodata/platforms.jsonand the website's vendored copy need nothing.Verified here
check-docs-drift.shclean — and proven live rather than assumed: a deliberate one-byte break of the openapi copy makes it fail, and it passes again restored.check-docs-links.shclean.android.ymlPlay notes gate run verbatim: 481/500 chars, unique against every other release's file.cmpidentical and stamped 0.31.4.cargo fmt --all --checkclean; the repopre-pushhook run manually, exit 0.git diff v0.31.3..HEADempty oninclude/punktfunk_core.h,crates/pf-driver-proto,punktfunk-core/src/lib.rs,punktfunk-host/src/events.rsandapi/openapi.json— direct evidence for five version rows.git diff v0.31.1..HEADon the C header also empty, which is the "third release running" claim.Cargo.lock's 36 workspace version strings moved withCargo.toml; zero0.31.3strings remain. 27 crate dirs / 39 workspace members, both unchanged.## For developers, and zero hits scanning for gamescope/polkit/logind/DPMS/CRTC/DRM/systemd/kwin/mutter/wlroots/rustls/ioctl.src/tag/v0.31.4.Not run here, and why
Any
punktfunk-hostbuild, clippy orcargo test— the host does not compile on macOS at all, and CI covers it. Theweb/anddocs-site/bun builds — nothing underweb/is touched by this commit and the docs-site edits are prose in an existing.md. The Android unit tests — nothing here touches Kotlin.After merge
git tag -a v0.31.4 … && git push origin v0.31.4— whichever workflow wins the create race seeds the release body fromdocs/releases/v0.31.4.md.docs/releases/README.md: match your own merge sha in the run list, because "CI ran" is not "your commit ran".announceworkflow with the tag.