# CI/CD

Speedwave's checks, builds, and releases all run through GitHub Actions. Workflow files live in `.github/workflows/`, and most pin the actions they call to a commit SHA with a version comment, for example `actions/checkout@de0fac2e... # v6.0.2`. The two Claude Code workflows are the exception: they use floating tags like `actions/checkout@v4` instead.

## What runs on every push and pull request

`test.yml` runs on push and on pull requests targeting `main` or `dev`, canceling superseded runs. It has seven jobs:

| Job | Runner | What it does |
| --- | --- | --- |
| `lint` | macos-latest | Rust clippy and format check, Prettier, MCP TypeScript type-check, MCP ESLint |
| `test` | macos-latest | Rust tests, transcription tests, MCP tests with coverage, MCP Office Python tests, entrypoint bats tests |
| `desktop` | macos-latest | Desktop clippy, Angular ESLint and tests with coverage, desktop config and release-gate tests, Tauri build tests |
| `audit` | ubuntu-latest | `npm audit` for the MCP servers and desktop app, `cargo audit` for both Rust workspaces |
| `swift` | macos-latest | Pull requests only: builds universal native macOS CLI binaries, then runs the Swift tests |
| `runtime-windows` | windows-latest | Rust tests for the Lima, WSL, build, and job-object modules, plus a check that shell scripts stay LF-clean on Windows checkouts |
| `desktop-windows-check` | windows-latest | Compiles the desktop app for Windows to catch Windows-only errors, without producing a full bundle |

The macOS-only jobs run there because part of the Rust workspace only compiles on macOS and Windows. For what the coverage numbers mean and where the thresholds live, see [testing](/docs/contributing/testing/).

## Building the desktop app

`desktop-build.yml` runs on pull requests and on push to `main` and `dev`, only when relevant paths change: desktop code, Rust crates, MCP servers, containers. A pull request builds macOS on Apple Silicon only, while a push also covers Intel and Windows. Without a signing key, for example on a fork's pull request, you get an unsigned bundle instead of a failed build.

## Releasing

`release-please.yml` runs on push to `main` and manages the release pull request, its version bump, and its tag. Merging that pull request triggers `desktop-release.yml`, which:

1. Validates the version and finds the matching draft release.
2. Builds and signs the desktop app for macOS (both architectures) and Windows, then notarizes the macOS build.
3. Cross-compiles the CLI for macOS and Windows and attaches the archives to the release.
4. Verifies every expected asset is present, then flips the release from draft to live. If that final check fails, the release goes back to draft.

A missing signing secret skips that step instead of failing the workflow, leaving the artifact unsigned or un-notarized. See [release process](/docs/contributing/release-process/) for the full setup and what ships in a release.

Two more workflows keep `Cargo.lock` and `package-lock.json` current on release branches, and a backmerge workflow folds `main` back into `dev` after each release, directly or through a pull request when `dev` has diverged.

## Release gates

Two workflows guard what can merge toward a release, and you'll meet both on any pull request into `main` or `dev`:

- `pr-title.yml` checks that your pull request title follows the conventional-commit format.
- `merge-strategy-check.yml` runs on pull requests to `main` and requires a release-triggering conventional-commit title before it lets you squash-merge, with an exemption for the automated release and backmerge pull requests.

Together these are why a merge to `main` reliably produces the release note and version bump you expect. See [release process](/docs/contributing/release-process/) for how commit titles map to version bumps.

## Dependencies and review

A Dependabot config manages dependency updates, and a companion workflow rebases its open pull requests after each push to `main`. Two Claude Code workflows also run in CI, both restricted to a fixed list of maintainers: one responds when a pull request, issue, or comment mentions `@claude`, and one reviews pull requests automatically in read-only mode.