Skip to content

Contributing

mrdulasolutions edited this page May 13, 2026 · 1 revision

Contributing

PRs welcome. This page covers conventions, code style, and what reviewers look for.

Before opening a PR

All four must pass locally — CI enforces the same gates and will reject the PR otherwise:

npm run lint && \
npm run typecheck && \
npm run typecheck:sidecar && \
npm run test:sidecar

For larger changes (migrations, multi-file refactors, architectural pivots), please open a discussion or a draft PR first so we can talk shape before you sink time.

Branches

  • Branch from main.
  • Branch names follow <author>/<short-description> — e.g. matt/imap-folder-cache, claude/keychain-fix.
  • Keep branches focused. One concern per PR. If you find yourself fixing three things, that's three PRs.

Commit messages

Conventional Commits format. Examples from the repo's history:

fix(sidecar/copy-runtime-modules): best-effort .node signing
feat(settings): consolidate AI Models picker into Agent Tools
ci(release): clear cached tauri bundle outputs before bundling
chore(release): bump to v0.1.8
docs(developer): update release section to reflect automated CI flow

Types: feat, fix, chore, docs, refactor, test, ci, build, perf. Scopes are open — pick something specific (e.g. sidecar/agents, not sidecar).

Body should explain why, not just what. The diff shows what. If the change is subtle (workaround for a macOS bug, etc.), put the explanation in the commit message so the next person reading git blame knows.

Code style

TypeScript

  • Never any. Fix the actual type instead. If a library has bad types, write a typed wrapper.
  • No as assertions outside type guards. Use if (typeof x === "string") or a z.parse, not as string.
  • Zod schemas for runtime validation. Anything coming from disk, network, or IPC.
  • ts-pattern for match-style conditionals.
  • Prefer narrow types. string | null is fine; string | null | undefined usually means you haven't decided what "absent" means.

React

  • Reduce state, effects, refs whenever possible.
  • URL is state — react-router state over component state where relevant.
  • Server state in react-query, not useState. Let the library handle caching, loading, errors.
  • Derive values instead of syncing state — if you can compute it, don't store it.

Ruby (no Ruby in this project, included for general principle)

  • Without type safety, simplicity matters more. Narrow interfaces, explicit arguments, simple data structures.

Rust

  • We have very little Rust — the shell is small and most of it is tauri- plugin invocations. If you're adding Rust, follow standard idioms; clippy passes; no unsafe without a comment explaining why.

General

  • Reduce state, coupling, complexity, code — in that order. See HN discussion.
  • Use library features when available. Skeptical about reinventing solved problems.
  • Design interfaces before implementation — think about how the consumer uses it first.
  • Follow existing patterns; look at similar files before introducing a new pattern.
  • Comments explain why, not what. Especially when the code is unintuitive.

Robustness

  • Use proper APIs (URL parsing, not substring matching).
  • Validate at boundaries. Trust internal code.
  • Required values should fail explicitly, not silently default.
  • "Error-first" with useful feedback for expected failure paths. But don't over-catch — wrapping everything in try/catch doesn't make code better.

Tests

  • New features should have at least one sidecar test (tests/sidecar/).
  • Cross-feature behavior (e.g. "smart-action archive flow") goes in tests/sidecar/integration/.
  • UI changes don't need a unit test in every case; an E2E smoke test (tests/e2e/) for the new path is usually right.
  • Tests should be stable — flaky tests get moved to tests/problematic/ and excluded from the main suite. We don't ship known-flaky tests as part of the main run.

Reviewing PRs

When reviewing someone else's PR, look for:

  1. Does it match the existing patterns in the file? Code that looks like an outsider wrote it ages faster than code that looks like the team wrote it.
  2. Is the change necessary? Smallest change that solves the problem wins.
  3. Are there hidden state changes? New useState, new module-level mutables, new caches — each one is a future debugging session.
  4. Tests for the new behavior, not just the new code? A test that exercises the function the PR adds is fine. A test that demonstrates "given X happens, Y is the observable outcome" is better.
  5. Comments explain the why? Especially for non-obvious branches, workarounds, version-specific code.

What lives in the codebase vs the wiki

  • In the codebase: anything that should evolve with the code (architecture decisions tied to a specific file, prompt template descriptions, etc.). Use the docs/ folder.
  • In this wiki: operational guides (how to install, troubleshoot, release). Things a user might read separately from the code.
  • In CLAUDE.md: agent collaboration patterns specific to AI tools (Claude Code) operating on this codebase.

When in doubt, codebase. The wiki is a satellite.

Reporting bugs

See Troubleshooting for the diagnostic flows first. If you're past those:

  1. Check ~/Library/Application Support/AOS Mail/sidecar.log for stack traces.
  2. Open an issue at https://github.com/mrdulasolutions/AOS-Mail/issues with:
    • macOS version
    • AOS Mail version (Settings → About, or grep version package.json from a source checkout)
    • The relevant 20-50 lines of sidecar.log
    • Steps to reproduce
  3. The logger redacts mail bodies, subjects, and prompts automatically — but glance over before posting.

Security disclosures

Email matt@mrdula.solutions or open a private security advisory at https://github.com/mrdulasolutions/AOS-Mail/security/advisories. Do not file public issues for security problems.

License

By contributing, you agree that your contributions will be licensed under BUSL-1.1, the same license as the project.

Clone this wiki locally