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.