# Contributing
Thanks for looking at this. Contributions land regularly and are genuinely
welcome — this file exists so review spends its time on your idea rather than
on the same handful of mechanical things.
## Before you open a PR
Run the gate. It is the same one CI runs, and it catches almost everything a
review would otherwise send back:
```
make test # cargo test + the GNOME, KDE and Omarchy contract suites
cargo clippy --all-targets -- -D warnings
cargo fmt --all -- --check
cargo machete # no unused dependencies
```
`make test` rather than `cargo test`: the frontends have their own Node contract
tests, and a change to the report shape can break them without touching Rust.
## Checklist
Most review round-trips come from one of these. None takes long.
- [ ] **`CHANGELOG.md` entry**, under `## [Unreleased]`, in the right category
(`Added` / `Changed` / `Fixed` / `Security`). Anything user-visible needs
one — a new provider, a changed default, a fixed bug, a renamed flag.
**Never edit a released section**; if your branch is older than the last
tag, git will merge your entry cleanly into whatever now sits at that
position, which has silently rewritten shipped history twice.
- [ ] **Tests that fail without your change.** For a bug fix, confirm the new
test fails on `main` and passes on your branch — say so in the PR.
- [ ] **No leftovers.** If your change removes the last caller of a helper,
remove the helper too. Dead shared machinery is how this codebase has
previously grown copies that drift apart.
- [ ] **Documentation that mentions what you changed.** Grep for it: `README.md`,
`config.example.toml`, `docs/configuration.md`,
`docs/vendor-endpoints.md`, `docs/format-placeholders.md`. A doc that
still describes the old behaviour is worse than one that says nothing —
especially when it promises a security property that is no longer true.
## Project rules worth knowing
These are in `CLAUDE.md` in full. The ones contributors hit most:
- **The widget always exits 0.** Waybar hides a module that doesn't. Errors
become a fallback `⚠` payload, never a non-zero exit.
- **No `PATH` lookups for a trusted binary** without a config override. The
executable that runs on every refresh should not be an ambient choice — see
`[supergrok] grok_binary` and `[copilot] gh_binary`.
- **Credentials are read, never rewritten**, unless the vendor owns the file.
CLI, editor and browser credentials are never parsed, copied, or stored.
- **Tests are hermetic.** A `#[test]` must never read or write a real `$HOME`
or `$XDG` path, or branch on an ambient environment variable — the AUR
package runs `cargo test` during install, so a test coupled to your machine
fails someone else's build. Inject the path: `Cache::at`, not `for_vendor`;
`creds::read_from`, not `default_path`.
- **Money is formatted in one place** (`format::money` / `format::usd`), and
a fetch outcome is built in one place (`outcome::Outcome`). Guard tests fail
the build if a second copy appears.
## Adding a provider
The bar is: **quota reachable with a credential the user already has, obtained
the way this project obtains credentials** — an API key, an OAuth file an
official CLI wrote, or an official CLI invoked for a token. Scraped browser
sessions do not qualify. `docs/vendor-endpoints.md` records providers that have
been evaluated and why some were declined; check it before starting.
Open an issue first with the endpoint, the auth mechanism, and a real response
capture with the numbers redacted. Field names and nesting are what a parser
gets pinned to, and a paraphrase is not enough to build against.
## Changing the tray popover UI
The popover is the part of the project with the most opinions, and all of
them are valid: some people want every provider at a glance, others one at a
time in their system's own look. To keep one person's taste from replacing
another's, UI changes follow one rule: **add, don't replace.**
- A different look is a new **popover style** (Settings → Appearance →
Popover Style), not a rewrite of Classic or Native. Scope its CSS to the
style (tokens on `body:has(.<style>-panel)`, rules under `.<style>-panel` in
`windows/popover/src/index.css`) and reuse the shared components; do not
fork `ProviderSection`, the Settings rows or the menus.
- Native follows the host: a shared glass base, refined per OS under
`.native-panel[data-os="<os>"]` (macOS after Apple's macOS 26 UI kit,
Windows after Fluent). Making
Native match another system — a GTK or Omarchy theme, should a Linux host
ever share this popover — is adding that OS's tokens, not a new style.
- A style changes presentation only. Features, data and settings live in the
shared components and work in every style; what a host cannot do is gated
by the OS, never by the style.
- Do not change a default (style, width, menu-bar look) in the same PR as a
new look. Propose it separately, with the reason.
- **Show a visual change.** Anything that changes how the popover, the menu
bar or the tray icon looks puts before/after screenshots in the PR
description, for every style, OS and theme it touches. A look cannot be
judged from a diff: screenshots let it be reviewed without building it, and
nobody finds out after an update. Fixes and features that apply to every
style are welcome as usual; say which styles you checked.
- Open an issue first for a new style: a screenshot or mockup and who it is
for is enough to agree it belongs before anyone writes it.
The frontends already work this way one level up — Waybar, GNOME, KDE,
Omarchy and the TUI all read the same `usage --json` and each looks the way
its users want. Styles bring that inside the popover.
## Platform reality
CI builds Linux, macOS and Windows, but the maintainer works on Linux. macOS
code paths — the Keychain, Claude Desktop, `safe_storage` — get compiled but
not exercised. If you are on a Mac and can test a change there, say so in the
PR; that is worth more than it sounds.