# Release runbook

> The generic shape of how a release ships. The spine is fixed (changelog · bump ·
> regenerate docs · tag · GitHub Release · docs publish); signing / publish specifics
> below are spt-mobile's.

## One-time setup

- Generate the Android release keystore locally (`keytool -genkeypair`); it lives OUTSIDE
  the repo on the dev machine (never in CI, never a repo secret). CI builds are
  debug-signed; release APKs are signed locally.
- Enable GitHub Pages (deploy from Actions) for the docs-publish workflow.

## Per release

1. **Bump** the version — single semver, kept in lockstep across `app/build.gradle.kts`
   (`versionName`, monotonic `versionCode`), `host/Cargo.toml`, and `rust/Cargo.toml` —
   if needed. No generated reference docs yet (docs-drift gate only); land everything green.
2. **Write the user-facing changelog** — add a `## [<version>] - <date>` section at the top of
   `CHANGELOG.md` with **Added / Changed / Fixed** subsections. This section becomes the GitHub
   Release **body** verbatim, so it is the changelog every user reads. Rules:
   - **User-facing UX only.** What a person using the product notices or does differently. Name
     the actual commands / flags / surfaces they touch.
   - **No internal lingo** — no requirement ids, internal module names, commit hashes, or
     milestone / hazard codes. A reader who has never seen the source must understand every line.
   - **Flag breaking changes** prominently under Changed.
   - The release **fails loudly** if the tagged version has no `## [<version>]` section — the
     changelog is not optional.
3. **Tag**: `git tag <vX.Y.Z> && git push origin <vX.Y.Z>`. This triggers:
   - the **release workflow** — build artifacts, extract this version's `CHANGELOG.md` section
     into the release body, and create the **GitHub Release on this same repo** with the built
     artifacts + checksums;
   - the **docs-publish workflow** — build `docs-site/` and publish to GitHub Pages (drift-gated;
     see `docs/DOCS-STRATEGY.md`).
4. **Sign + publish**: self-sign the release APK locally with the release keystore
   (`apksigner sign`), verify against the workflow's checksums, attach the signed APK +
   signatures to the draft release alongside the host-binary artifacts, then publish.

## Notes

- **Three independent numbers — never conflate them:** the **semver** (`vX.Y.Z`, shared by
  app + host binary + rust slice), the Android **`versionCode`** (a monotonic install
  counter — bumps every published APK, carries no semantic meaning), and the
  **device-link protocol version** (the pairing/device-link ALPN — bumps only on wire
  changes, gates phone↔host compatibility).
- Trust model: phone↔host trust is established at pair time (QR: node key + token), not at
  install time — an APK update never changes pairings. Android signature continuity gates
  APK updates; a leaked release keystore means a new keystore + fresh installs (pairings
  survive, they live in app data).
