# 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 + GNOME, KDE, Omarchy and Mint 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 Node and Python 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(.