-
Notifications
You must be signed in to change notification settings - Fork 0
Contributing
PRs welcome. This page covers conventions, code style, and what reviewers look for.
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:sidecarFor 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.
- 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.
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.
-
Never
any. Fix the actual type instead. If a library has bad types, write a typed wrapper. -
No
asassertions outside type guards. Useif (typeof x === "string")or az.parse, notas string. - Zod schemas for runtime validation. Anything coming from disk, network, or IPC.
-
ts-patternfor match-style conditionals. - Prefer narrow types.
string | nullis fine;string | null | undefinedusually means you haven't decided what "absent" means.
- 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.
- Without type safety, simplicity matters more. Narrow interfaces, explicit arguments, simple data structures.
- 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; nounsafewithout a comment explaining why.
- 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.
- 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.
- 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.
When reviewing someone else's PR, look for:
- 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.
- Is the change necessary? Smallest change that solves the problem wins.
-
Are there hidden state changes? New
useState, new module-level mutables, new caches — each one is a future debugging session. - 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.
- Comments explain the why? Especially for non-obvious branches, workarounds, version-specific code.
-
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.
See Troubleshooting for the diagnostic flows first. If you're past those:
- Check
~/Library/Application Support/AOS Mail/sidecar.logfor stack traces. - Open an issue at https://github.com/mrdulasolutions/AOS-Mail/issues with:
- macOS version
- AOS Mail version (Settings → About, or
grep version package.jsonfrom a source checkout) - The relevant 20-50 lines of
sidecar.log - Steps to reproduce
- The logger redacts mail bodies, subjects, and prompts automatically — but glance over before posting.
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.
By contributing, you agree that your contributions will be licensed under BUSL-1.1, the same license as the project.