Contributing Workflow
This page describes the end-to-end flow of contributing a change to discord-bot-rs, from the moment you have an idea to the moment your change ships in a release. It complements the top-level CONTRIBUTING.md, which is the canonical short version — read that first. This page adds the in-the-weeds detail you don’t want to bury in a root-level file.
Before you start
Read the ground rules. Be kind, keep PRs focused, and if you’re planning anything substantial — a new feature, a module reshuffle, a dependency bump with downstream impact — open an issue first so nobody spends a week on an approach that gets rejected at review.
Check existing issues and PRs before opening a duplicate. Search issues and pull requests.
Bugs go through the bug report template: version/commit, reproduction steps, redacted logs, deployment method. Filling it out honestly is worth more than any clever fix — half the time the steps you write reveal the bug.
Feature requests go through the feature template. Describe the problem before the proposal.
Security issues go through SECURITY.md — don’t open a public issue for anything that could be a vulnerability.
Fork and branch
The project follows the standard GitHub fork + feature-branch flow.
- Fork MrMcEpic/discord-bot-rs.
- Clone your fork locally.
- Add the upstream remote:
git remote add upstream https://github.com/MrMcEpic/discord-bot-rs.git - Create a feature branch off
master:git checkout -b fix/wordle-expiry-bug master
Branch names don’t have a strict format, but descriptive ones like
feat/stock-alerts, fix/music-skip-deadlock, or docs/add-mcp-guide
are easier to review than patch-1.
Local setup
Follow Building Locally to get a working
cargo run. The short version: install the Rust stable toolchain,
make sure Docker and Docker Compose are available for Postgres, and
run cargo check plus
cargo check --manifest-path mcp-gateway/Cargo.toml to pull down
dependencies and confirm the tree compiles.
If you don’t have a Discord application yet, follow the Prerequisites page; you’ll need it to test your changes live.
Make the change
Follow the posture of the file you’re editing — the existing code is the style guide. When in doubt:
- Add a test if reasonable. The crate ships with ~120 automated
tests (92 unit in main, 10 in the gateway, 18 Postgres-backed
integration tests under
tests/). Pure logic and SQL queries are well-covered; Discord-context handlers and the voice pipeline aren’t, so PRs that move the needle there are particularly welcome. For a bug fix, a test that reproduces the bug is the best comment on the diff. See Testing for the patterns. - Keep the PR focused. One logical change per PR. A refactor and a feature and a docs rewrite are three PRs, not one.
- Update the docs. If you changed a command, update the
command list. If you changed a
feature’s behaviour, update its page under
docs/features/. New config options go in instance-config.md. - Add a CHANGELOG entry. User-visible changes go under
[Unreleased]in CHANGELOG.md in one ofAdded,Changed,Fixed, orRemoved.
Commit style
The project leans toward conventional-style prefixes but doesn’t enforce them with a hook. Common prefixes:
feat:— a new user-visible feature or commandfix:— a bug fixdocs:— documentation onlychore:— build, CI, deps, or repo housekeepingrefactor:— code change with no behaviour changetest:— adding or fixing tests
One logical change per commit. If you’re tempted to write “and also”
in the commit message, split it with git reset or git add -p.
Don’t force-push to master on your fork — that’s fine on your own
feature branches, but never anywhere shared.
Pre-PR checklist
Before you push and open a PR, run through:
-
cargo fmt -
cargo fmt --check(same thing, but catches files you forgot to stage) -
cargo clippy --all-targets -- -D warnings -
cargo test --bins(minimum — unit tests, no Postgres needed) -
cargo testwith aDATABASE_URLpointing at a Postgres (full — runs the integration tests undertests/). Easy throwaway:docker run -d --rm -p 5433:5432 -e POSTGRES_USER=test -e POSTGRES_PASSWORD=test -e POSTGRES_DB=test postgres:17, thenDATABASE_URL=postgres://test:test@localhost:5433/test cargo test. See Testing for the long version. - For changes touching the gateway crate, the same three commands
again with
--manifest-path mcp-gateway/Cargo.tomlor from insidemcp-gateway/(the gateway has no DB-backed tests, socargo testis enough) - Docs updated if behaviour changed
- CHANGELOG entry under
[Unreleased] - Manual test in a live Discord server
CI will run the first four for you, so skipping them locally just means you find out about failures from a bot instead of a shell. It’s faster to catch them yourself.
Open the pull request
Push your branch and open a PR against master:
git push -u origin fix/wordle-expiry-bug
Use the PR template. It asks for:
- Summary — one to three sentences on what and why.
- Changes — a bullet list of the main edits.
- Testing — the testing checkboxes (fmt, clippy, test, manual) and a short description of what you manually verified.
- Related issues —
Closes #123for issues this PR fully fixes,Refs #456for related context. - Breaking changes — default is
None. If yours breaks an existing config, command, or behaviour, describe the migration. - Checklist — four housekeeping items; tick them honestly.
Mark the PR as draft if it isn’t ready for review yet. Draft PRs still get CI, so you can push through a broken state until it’s green without fielding review comments prematurely.
CI checks
When you push, CI runs the
ci.yml
workflow, which has four jobs:
- check-main —
cargo fmt --check,cargo clippy --all-targets -- -D warnings,cargo check --all-targets,cargo teston the main crate. The job stands up apostgres:17service container with a health check and exportsDATABASE_URLso the integration tests undertests/run for real against a live database. - check-gateway — the same four commands inside
mcp-gateway/(no Postgres service; the gateway’s tests are pure). - docker-main — builds the top-level
Dockerfile(no push). - docker-gateway — the same for
mcp-gateway/Dockerfile.
A red check fails the PR. Common failures:
cargo fmt --checkdiffers — runcargo fmtand push.- Clippy flags something — read the lint and fix it, or add a
targeted
#[allow(...)]with a comment if it’s a false positive. - Compile error on Linux that didn’t happen locally — usually a
missing system library.
check-maininstallscmake,libopus-dev, andlibsodium-dev. - Test flake — rare; push an empty commit or ask for a re-run.
Re-run a workflow by pushing any commit or re-opening the PR.
Review process
Reviews usually come within a few days. If a week goes by with no response, comment on the PR to bump it — it’s almost certainly been missed, not ignored.
During review:
- Respond by committing, not force-pushing. Follow-up commits make it easy for the reviewer to see exactly what changed between passes.
- Don’t squash your own history unless asked. The maintainer squashes at merge time.
- Mark resolved conversations once you’ve addressed them.
Merge
The default merge strategy is squash merge. Your feature branch
becomes one commit on master, titled after the PR title (and — since
the title follows a conventional prefix — grouped cleanly in the
changelog).
This means the shape of your individual commits matters less than the shape of the PR description and title. A messy WIP history is fine as long as the squashed commit message is tidy.
After the merge
Once your PR is merged:
git checkout master
git fetch upstream
git merge --ff-only upstream/master
git push origin master
git branch -d fix/wordle-expiry-bug
Then rebase any other in-flight feature branches onto the new master.
The merge UI also offers a button to delete the remote branch.
Release cadence
Releases are cut as-needed — usually every few weeks, sooner for security fixes. Every merged PR ends up in the next release’s CHANGELOG.md entry, and the release workflow publishes a tagged build. If your change is urgent, say so in the PR description.