From ad6b5184dcc8aa938555631f40b235371bd4767b Mon Sep 17 00:00:00 2001 From: enricobuehler Date: Fri, 28 Aug 2026 23:36:09 +0200 Subject: [PATCH] docs: adopt the Punktfunk house style Three writing surfaces were being asked to hold design reviews. Commit subjects on main averaged 101 characters, CHANGELOG.md ran to 6,090 lines for eight versions, and module rustdoc carried milestone codes and soak durations that git blame already keeps honest. The rules now live in docs/writing.md: Conventional Commits with a 72-character cap, Keep a Changelog categories for new sections, and comments that state an invariant rather than recap a diff. CONTRIBUTING, AGENTS.md and the Gitea PR template point at it, and the CHANGELOG header says which form new sections take. Nothing existing is rewritten. The changelog archive, the release notes and every current rustdoc header stay as they are; the style applies to what gets written next. Source: github.com/luxus/punktfunk-house-style --- .gitea/PULL_REQUEST_TEMPLATE.md | 6 +- AGENTS.md | 14 ++ CHANGELOG.md | 5 + CONTRIBUTING.md | 18 ++- docs/writing.md | 236 ++++++++++++++++++++++++++++++++ 5 files changed, 276 insertions(+), 3 deletions(-) create mode 100644 docs/writing.md diff --git a/.gitea/PULL_REQUEST_TEMPLATE.md b/.gitea/PULL_REQUEST_TEMPLATE.md index 4640cb0da..2d0e9ba7b 100644 --- a/.gitea/PULL_REQUEST_TEMPLATE.md +++ b/.gitea/PULL_REQUEST_TEMPLATE.md @@ -1,4 +1,8 @@ - + + + **User-facing fact changed?** (an install step, a knob, a port, what a feature does, a limit) → the docs-site page that owns it is updated in this PR, or this is n/a. Install/repo/port facts diff --git a/AGENTS.md b/AGENTS.md index 77b8e4e11..5f0d6534f 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -2,6 +2,20 @@ Guidance for coding agents working in this repository. +## Writing standards + +Read `docs/writing.md` before you write a commit message, a `CHANGELOG.md` entry, or a comment. +It is the house style for all three, and §4 is a per-PR checklist. The short version: + +- Commit subject is `type(scope): summary` — imperative, **72-character hard cap**, no trailing + period, one logical change. No `Co-Authored-By` trailer. +- The commit body is *why*, wrapped at 72. The investigation, the measurements and the rejected + paths go on the pull request, never in the message. +- Write the Gitea PR title as a conventional commit; Gitea makes it the merge subject. +- New `CHANGELOG.md` sections use Keep a Changelog categories. Leave the older sections alone. +- A comment states an invariant or a trap. A comment never enforces a trust boundary — a type, + a test or an assertion does. + ## Agent skills ### Issue tracker diff --git a/CHANGELOG.md b/CHANGELOG.md index 1d16c66d4..8f138164d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,11 @@ long enough to bury the user-facing half it was appended to. See `docs/releases/ If you embed `punktfunk-core`, package Punktfunk, or write a plugin, this file is for you. Start with the version table of the release you are moving to, then read **Breaking changes**. +New sections use [Keep a Changelog](https://keepachangelog.com/) categories — `Breaking`, +`Added`, `Changed`, `Fixed`, `Security` — plus the version table, and stay to one screen for a +patch and two for a minor. Sections written before 28 August 2026 predate that rule and are left +as they are. See `docs/writing.md` §2. + --- ## v0.32.0 diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 4df614af1..572e08b6f 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -118,10 +118,24 @@ must still exist in the tree, the counts of undocumented `PUNKTFUNK_*` variables `punktfunk-host` subcommands may never grow (document the new knob, or consciously raise the baseline in the script), and internal docs links must resolve. -Match the surrounding code's comment density and naming. Commit messages end with the -`Co-Authored-By` trailer (see `git log`). +Match the surrounding code's comment density and naming. See the [README's Build & test section](README.md#build--test-from-source) for the extra dev commands (the FEC loss harness, the standalone C-ABI proof) and [Design invariants](README.md#design-invariants) for the rules a change is expected to hold to, and the [docs site](https://docs.punktfunk.unom.io) for architecture and per-platform guides. + +## How to write it (commits, changelog, comments) + +House style lives in **[docs/writing.md](docs/writing.md)**, which also carries a per-PR checklist. +The three rules you need before your first commit: + +- **Commit subjects follow [Conventional Commits](https://www.conventionalcommits.org/) — + `type(scope): summary`, 72-character cap, imperative, no trailing period.** The *why* goes in + the body, wrapped at 72. The investigation goes on the pull request, not in the message. A Gitea + PR title becomes the merge subject, so write the PR title as a conventional commit too. +- **New `CHANGELOG.md` sections use [Keep a Changelog](https://keepachangelog.com/) categories** + — `Breaking` / `Added` / `Changed` / `Fixed` / `Security` — plus the version table. Keep the + existing sections as they are. What a *user* can do goes in `docs/releases/vX.Y.Z.md` instead. +- **A comment states an invariant or a trap, not a recap of the diff.** If a trust boundary + matters, a type, test or assertion has to enforce it — a comment alone never does. diff --git a/docs/writing.md b/docs/writing.md new file mode 100644 index 000000000..2b7a8a0be --- /dev/null +++ b/docs/writing.md @@ -0,0 +1,236 @@ +# Punktfunk writing standards + +House style for **commits**, **changelogs**, and **comments**. + +Audited against [unom/punktfunk](https://git.unom.io/unom/punktfunk) on 28 August 2026. The engineering in that tree is careful. The writing is careful too. The problem is the medium: git log, CHANGELOG.md, and rustdoc are being asked to hold design reviews. + +This document is the rulebook. The companion site shows the originals next to rewrites. + +--- + +## 0. The one sentence + +**Put each fact where someone can find it later.** + +| Fact | Lives in | +| --- | --- | +| What changed, in one greppable line | Commit **subject** | +| Why it changed, in a short body | Commit **body** (and the PR if it needs a diagram) | +| What a user can do now | `docs/releases/vX.Y.Z.md` | +| What an embedder must do | `CHANGELOG.md` | +| The investigation, measurements, rejected paths | Pull request and `docs/adr/` | +| The invariant that must remain true | Comment, type, or test | + +If you are writing a novel, you are in the wrong file. + +--- + +## 1. Commits + +### Shape + +``` +type(scope): imperative summary + +Why it failed for a user. What was actually wrong. +What you changed. Wrap at 72. + +Fixes #123 +``` + +- **50 characters** is the aim for the subject. **72 is the hard cap.** +- No trailing period on the subject. +- Imperative, present tense: `keep`, `skip`, `advertise` — matching `git merge` / `git revert`. +- One logical change per commit. A subject with “and” is two commits, or one theme named as a theme. + +### Types + +| Type | Use | +| --- | --- | +| `feat` | User-visible capability that did not exist | +| `fix` | A bug. Put the symptom in the body | +| `docs` | Docs, comments-as-docs, release notes. No behaviour change | +| `refactor` | Same behaviour, different shape | +| `perf` | Same behaviour, cheaper. Name the metric if you have one | +| `test` | Tests only | +| `chore` | Deps, version bumps, generated files | +| `ci` | Pipelines and gates | +| `security` | Trust-boundary changes | + +Scopes are subsystem names a newcomer would grep: `host`, `hyprland`, `mdns`, `abr`, `console`, `gamestream`, `android`, `web`, `core`. + +### Why the current style fails + +Recent subjects on main (28 Aug 2026): + +| As written | Problem | Rewrite | +| --- | --- | --- | +| The retry loop stops eating the restore that re-lights the desk | Metaphor, no scope, 69 chars of plot | `fix(host/hyprland): keep topology restore across pipeline retries` | +| The advert names its address, and the client stops rolling dice on the rest | Pun, two clauses, unsearchable | `fix(mdns): advertise a primary address so clients stop guessing` | +| The streamed head can be focused on a Lua box, which is what makes it produce frames | Relative clause, slang | `fix(hyprland): focus the virtual head on Lua-configured compositors` | +| The Omarchy box says otherwise: five things the plan got wrong, measured | Lab-notebook title | `docs(omarchy): correct five host-plan assumptions from measured hardware` | +| The ticket parser proves its own segments exist | Anthropomorphic, no scope | `fix(web): narrow ticket-parser types so segments are proven present` | + +The last 200 subjects average **101 characters**. Longest: **211**. At the time of the audit, CONTRIBUTING.md’s entire commit rule was “end with the Co-Authored-By trailer.” + +### Body rules + +- Three short paragraphs is enough: user-visible failure, actual cause, what you changed. +- Wrap at 72. +- Do not paste CI logs, soak minutes, GPU SKUs, or RFC section numbers. Link the PR or the ADR. +- No `Co-Authored-By` trailer in this repo. Attribution is off, because Gitea 1.27 promotes + the trailer to a second participant on the commit page. Credit a co-author in prose. + +### Pull requests + +The forensic essay is valuable. **Put it on the PR.** The merge commit subject is the conventional subject of the work, not the essay’s headline. Gitea PR titles become merge subjects — write the PR title as a conventional commit. + +--- + +## 2. Changelogs + +Punktfunk already split the two audiences at v0.25.0. Keep that. Stop writing both files in the same voice. + +### `docs/releases/vX.Y.Z.md` — people who stream + +Keep the existing template: + +1. Compatibility line (plain language, no ABI numbers) +2. `## TL;DR` — three to six one-line bullets +3. `## Before you update` — only if the reader must act +4. `## New` / `## Improved` / `## Fixed` / `## Security` +5. `## For developers` — one link to CHANGELOG.md at the **tag** + +Voice rules already in `docs/releases/README.md` are correct. Follow them. Do not narrate lab sessions (“we watched one do exactly that on a local network, unprompted”) in the user notes. Say the default changed, and that clients that support it opt in. + +### `CHANGELOG.md` — embedders, packagers, plugin authors + +Keep: + +- Newest first +- The **version table** (every row, including unchanged) +- Breaking changes with an action + +Replace: + +- Sentence-headings (“The auto-bitrate overhaul (four phases)”) +- 454-line sections (v0.32.0) +- Field-log storytelling, soak evidence, RFC chapter numbers + +Use [Keep a Changelog](https://keepachangelog.com/) categories: + +```markdown +## [0.32.0] — 2026-08-27 + +ABI 25 → 26 (additive). Wire protocol stays 2. + +### Breaking +- **Bitrate means the wire budget.** `live_bitrate` is no longer encoder rate. + FEC, framing and audio used to ride on top. Embedders that treated it as + encoder rate must stop. + +### Added +- `punktfunk_connect_opts` replaces the `connect_ex*` ladder. Every `ex` remains. + +### Fixed +- Automatic bitrate treated a still picture as congestion. + +### Security +- Authenticated console sessions could reach pairing without the console password. + Pairing grants launch. The routes now re-ask. +``` + +### Length + +| Release | Target | +| --- | --- | +| Patch | One screen | +| Minor | Two screens | +| Longer than that | Split, or link an ADR | + +`CHANGELOG.md` is currently **6,519 lines** for eight versions. That is not a changelog. It is an archive of design reviews. Move the reviews to `docs/adr/` and link them. + +--- + +## 3. Comments + +### Why + +Comments exist for the non-local reason: the invariant, the trap, the rejected alternative. Names and types are the what. If the next five lines already say it, delete the comment. + +### Module rustdoc + +A module header is a map, 8–20 lines: + +1. What it is +2. The public contract +3. How to choose / pin it +4. Where deeper evidence lives + +It is not a program-of-record. `crates/pf-client-core/src/video_vk_native.rs` opens with **8,940 characters** of `//!` before the first item: WP-C, M3 WP-2, M7, a 92-minute soak, an RTX 5070 Ti. That history is already in git and in `design/client-native-decode.md`. rustdoc should point there, not duplicate it. + +### A comment is not a spec + +The 2026-08-25 security review’s serious findings were documented promises the code had stopped keeping. One commit even named it: `the comments were the spec, and the code had drifted`. + +If a boundary matters, encode it: type, test, assertion, parser. Comments explain the boundary. They do not constitute it. + +### SAFETY, FFI, concurrency + +Keep these exact. This is already good: + +```rust +// SAFETY: the clipboard is open (the `Clip` guard); the handle returned is +// BORROWED from the clipboard and stays valid while it is open, so it is +// never freed here. +``` + +Do not restyle that into a narrative of how the bug was found. + +### History + +Dates, SKUs, soak durations, milestone codes, and “this used to” sentences go stale in the file. `git blame` and ADRs keep them honest. + +### Prefer a name + +`stash_topology_restore_first_wins` says what a twelve-line comment would. If you need a comment to explain a name, rename. + +--- + +## 4. Checklist (every PR) + +- [ ] Subject is `type(scope): summary`, ≤ 72 characters, imperative, no period +- [ ] Subject names a subsystem a newcomer would grep +- [ ] Body is why, not the investigation (investigation is on the PR) +- [ ] One logical change; no “and” holding two fixes together +- [ ] User-facing fact updated in `docs/releases/` or the docs-site page that owns it +- [ ] Embedder-facing fact is a bullet in CHANGELOG.md, not a new chapter +- [ ] New comments state an invariant or a trap, not a recap of the diff +- [ ] Module rustdoc still fits on one screen +- [ ] No new comment that is the only enforcement of a trust boundary + +--- + +## 5. What this does not ask + +- It does not ask anyone to write less carefully. It asks them to file the care in the right place. +- It does not ban long writing. It bans long writing in git subjects and changelog bullets. +- It does not replace `docs/releases/README.md`. That voice guide stays. This document covers the three surfaces that guide does not. + +--- + +## 6. Adoption + +Adopted 28 August 2026. Three steps landed with this file: + +1. This file is `docs/writing.md`, linked from CONTRIBUTING.md and AGENTS.md. +2. CONTRIBUTING.md’s commit rule points here. +3. `.gitea/PULL_REQUEST_TEMPLATE.md` asks for a conventional PR title, because a Gitea PR + title becomes the merge subject. + +Two steps are ongoing, and deliberately not a sweep: + +4. Write each **new** `CHANGELOG.md` section in Keep a Changelog form. The archive under the + older headings stays as it is; do not rewrite it. +5. **New** module files get the short rustdoc. Do not rewrite every existing header in one + pass — rewrite one when you are already changing that module. -- 2.54.0