Submitting changes
Commits, pull requests, and review.
Koda's contribution model is plain GitHub: fork, branch, pull request. The bar is clarity — commits should read well, PRs should explain what they change and why, and CI should be green before review.
Before you start
- Check open issues and pull requests for prior art.
- For non-trivial changes, open a discussion or draft issue first. It's cheaper to align on approach before code than after.
- Read
CONTRIBUTING.mdin the repo — it's short and authoritative.
Branches
- Branch from
main. Main always reflects the latest merged state. - Naming. Prefix with a short type plus a slug:
feat/memory-recall-timeout,fix/session-cookie-samesite,docs/skills-authoring. - Keep branches short. Rebase on
mainregularly. A branch that lives longer than a week usually has diverged more than it gains.
Commits
- Imperative, present-tense subjects — "Fix X" beats "Fixed X" beats "X is fixed".
- One logical change per commit. The commit is a unit of review; mixing unrelated changes makes
git logharder to read in six months. - Body explains why, not what — the diff already shows what changed. If the why is obvious, skip the body.
Pull requests
Description
The PR description should cover:
- What — a concrete summary of the change. Two or three sentences is usually enough.
- Why — the motivation, linked to an issue or discussion if one exists.
- How to verify — steps a reviewer can follow to convince themselves the change works.
- Risk — anything the change could break, or that operators should know about.
Scope
Keep PRs focused. A PR that changes one thing is easier to review, merge, and — if needed — revert. Mechanical refactors and the behavioural change they enable belong in separate PRs whenever possible.
Size
No hard cap, but anything over ~500 net-lines-changed benefits from being split. If a large change can't be split, pre-announce it in an issue so reviewers have context before opening the diff.
Local checks before opening
Run the full suite so the reviewer isn't the first to hit failures:
# Backendruff check .ruff format --check .mypy koda/ --ignore-missing-importspytest --cov=koda --cov-report=term-missing# Webpnpm lintpnpm typecheckpnpm testpre-push hook that runs ruff check, mypy, and the web typecheck catches 80% of CI failures before you push. The repo ships an opt-in sample you can enable with git config core.hooksPath scripts/hooks.Tests
- Every behavioural change needs a test — preferably at the layer the change lives (unit where possible, integration where necessary).
- Regressions take a test. Fixing a bug without a test lets the bug recur silently.
- Memory, knowledge, and runtime each have their own test subdirectories — follow the existing pattern rather than mixing.
Documentation
If your change alters user-visible behaviour, update the docs in the same PR. User-facing docs live in docs/; contributor- facing notes live in docs/ai/. The README is for headline claims — update it when your change materially shifts one of them.
Review
- Expect feedback. Reviews are a conversation, not a gate. Be prepared to iterate.
- Push updates as additional commits during review; squash only on the final merge. It makes the review diff easy to follow.
- Thank the reviewer. Review is work too.
Merging
Maintainers use squash-merge onto main by default. The final commit message is the PR title + a summary of the body, so keep both tidy.
Security-sensitive changes
If you're fixing a security issue, follow the disclosure process in SECURITY.md. Don't open a public PR with the fix before the disclosure window closes.
Next steps
- Development setup — if you haven't cloned yet.
- Code of conduct — the community standards every contribution is held to.